aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2014-10-07 11:32:14 +0200
committerMartin Braun <martin.braun@ettus.com>2014-10-07 12:09:33 +0200
commit1b149f561370687ad65e3aa644a402f00dbd16ea (patch)
treeab86042840fa1369d64bca56c5f3a64d1a4f1f72
parentfd3e84941de463fa1a7ebab0a69515b4bf2614cd (diff)
downloaduhd-1b149f561370687ad65e3aa644a402f00dbd16ea.tar.gz
uhd-1b149f561370687ad65e3aa644a402f00dbd16ea.tar.bz2
uhd-1b149f561370687ad65e3aa644a402f00dbd16ea.zip
Initial commit E300 support.
-rw-r--r--firmware/e300/rev_b/Makefile76
-rw-r--r--firmware/e300/rev_b/PMC.atsln29
-rw-r--r--firmware/e300/rev_b/PMC.cproj288
-rw-r--r--firmware/e300/rev_b/bq24190.c335
-rw-r--r--firmware/e300/rev_b/bq24190.h26
-rw-r--r--firmware/e300/rev_b/config.h2
-rw-r--r--firmware/e300/rev_b/debug.c297
-rw-r--r--firmware/e300/rev_b/debug.h90
-rw-r--r--firmware/e300/rev_b/error.h31
-rw-r--r--firmware/e300/rev_b/global.h49
-rw-r--r--firmware/e300/rev_b/i2c.c518
-rw-r--r--firmware/e300/rev_b/i2c.h17
-rw-r--r--firmware/e300/rev_b/io.c75
-rw-r--r--firmware/e300/rev_b/io.h31
-rw-r--r--firmware/e300/rev_b/ltc3675.c525
-rw-r--r--firmware/e300/rev_b/ltc3675.h37
-rw-r--r--firmware/e300/rev_b/ltc4155.c402
-rw-r--r--firmware/e300/rev_b/ltc4155.h25
-rw-r--r--firmware/e300/rev_b/main.c385
-rw-r--r--firmware/e300/rev_b/power.c909
-rw-r--r--firmware/e300/rev_b/power.h58
-rw-r--r--firmware/e300/rev_c/Makefile76
-rw-r--r--firmware/e300/rev_c/PMC.atsln29
-rw-r--r--firmware/e300/rev_c/PMC.cproj288
-rw-r--r--firmware/e300/rev_c/bq24190.c335
-rw-r--r--firmware/e300/rev_c/bq24190.h26
-rw-r--r--firmware/e300/rev_c/config.h2
-rw-r--r--firmware/e300/rev_c/debug.c297
-rw-r--r--firmware/e300/rev_c/debug.h90
-rw-r--r--firmware/e300/rev_c/error.h31
-rw-r--r--firmware/e300/rev_c/global.h49
-rw-r--r--firmware/e300/rev_c/i2c.c518
-rw-r--r--firmware/e300/rev_c/i2c.h17
-rw-r--r--firmware/e300/rev_c/io.c75
-rw-r--r--firmware/e300/rev_c/io.h31
-rw-r--r--firmware/e300/rev_c/ltc3675.c525
-rw-r--r--firmware/e300/rev_c/ltc3675.h37
-rw-r--r--firmware/e300/rev_c/ltc4155.c402
-rw-r--r--firmware/e300/rev_c/ltc4155.h25
-rw-r--r--firmware/e300/rev_c/main.c385
-rw-r--r--firmware/e300/rev_c/power.c900
-rw-r--r--firmware/e300/rev_c/power.h58
-rw-r--r--host/docs/mainpage.dox1
-rw-r--r--host/docs/usrp_e3x0.dox302
-rw-r--r--host/lib/convert/CMakeLists.txt3
-rw-r--r--host/lib/convert/convert_neon.S37
-rw-r--r--host/lib/convert/convert_with_neon.cpp36
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_e3x0.cpp64
-rw-r--r--host/lib/usrp/e300/CMakeLists.txt55
-rw-r--r--host/lib/usrp/e300/e300_async_serial.cpp245
-rw-r--r--host/lib/usrp/e300/e300_async_serial.hpp113
-rw-r--r--host/lib/usrp/e300/e300_common.cpp59
-rw-r--r--host/lib/usrp/e300/e300_common.hpp31
-rw-r--r--host/lib/usrp/e300/e300_defaults.hpp82
-rw-r--r--host/lib/usrp/e300/e300_eeprom_manager.cpp236
-rw-r--r--host/lib/usrp/e300/e300_eeprom_manager.hpp125
-rw-r--r--host/lib/usrp/e300/e300_fifo_config.cpp429
-rw-r--r--host/lib/usrp/e300/e300_fifo_config.hpp52
-rw-r--r--host/lib/usrp/e300/e300_fpga_defs.hpp29
-rw-r--r--host/lib/usrp/e300/e300_global_regs.cpp131
-rw-r--r--host/lib/usrp/e300/e300_global_regs.hpp78
-rw-r--r--host/lib/usrp/e300/e300_i2c.cpp409
-rw-r--r--host/lib/usrp/e300/e300_i2c.hpp77
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp1347
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp296
-rw-r--r--host/lib/usrp/e300/e300_io_impl.cpp599
-rw-r--r--host/lib/usrp/e300/e300_network.cpp642
-rw-r--r--host/lib/usrp/e300/e300_network.hpp43
-rw-r--r--host/lib/usrp/e300/e300_regs.hpp69
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.cpp148
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.hpp59
-rw-r--r--host/lib/usrp/e300/e300_sensor_manager.cpp289
-rw-r--r--host/lib/usrp/e300/e300_sensor_manager.hpp77
-rw-r--r--host/lib/usrp/e300/e300_spi.cpp127
-rw-r--r--host/lib/usrp/e300/e300_spi.hpp34
-rw-r--r--host/lib/usrp/e300/e300_sysfs_hooks.cpp121
-rw-r--r--host/lib/usrp/e300/e300_ublox_control.hpp50
-rw-r--r--host/lib/usrp/e300/e300_ublox_control_impl.cpp505
-rw-r--r--host/lib/usrp/e300/e300_ublox_control_impl.hpp457
-rw-r--r--host/utils/CMakeLists.txt7
-rw-r--r--host/utils/query_gpsdo_sensors.cpp21
-rw-r--r--host/utils/usrp_e3x0_network_mode.cpp80
84 files changed, 15959 insertions, 9 deletions
diff --git a/firmware/e300/rev_b/Makefile b/firmware/e300/rev_b/Makefile
new file mode 100644
index 000000000..0f13dc60b
--- /dev/null
+++ b/firmware/e300/rev_b/Makefile
@@ -0,0 +1,76 @@
+#
+# Copyright 2009 Ettus Research LLC
+#
+
+##################################################
+# Compiler
+##################################################
+CC = avr-gcc
+OBJCOPY = avr-objcopy
+STRIP = avr-strip
+OBJDUMP = avr-objdump
+SREC = srec_cat
+CFLAGS = -Os -std=gnu99 -Wall -fshort-enums -pedantic-errors -Wl,--gc-sections \
+ -Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow \
+ -DENABLE_SERIAL -DCHARGER_TI -DLED_POLARITY -DDEBUG_VOID
+# -DENABLE_SERIAL : Output serial debug
+# -DCHARGER_TI : Use TI charger (rev B) instead of LTC (rev A)
+# -DLED_POLARITY : Dual-polarity LED on rev B
+# -DDDR3L : Lower DDR voltage (rev B R-divider changed, so disable this to get back into nominal range)
+# -DDEBUG_VOID : Use (void) debug function replacements instead of inline NOOP (use if .text overflows)
+# -DI2C_REWORK : Rev A only
+# -DDEBUG : Enable debug routines (LED blinks, etc)
+# -DDEBUG_SAFETY : Extra debug prints
+# -DATTINY88_DIP : ATTINY88 DIP testing on STK600
+# -DHARDWIRE_ENABLE : LTC3675 dedicated enable lines - don't use
+
+#-Werror
+#-D IO_DEBUG
+
+##################################################
+# Files
+##################################################
+HDRS =
+SRCS = main.c io.c power.c ltc3675.c i2c.c debug.c bq24190.c
+TARGET = main
+
+##################################################
+# Device
+##################################################
+MMCU = attiny88
+#PROGRAMMER = avrisp2
+PROGRAMMER = stk600
+PORT = usb
+AVRDUDE = avrdude -p $(MMCU) -c $(PROGRAMMER) -P $(PORT)
+
+##################################################
+# Global Targets
+##################################################
+all: $(TARGET).hex
+
+clean:
+ $(RM) *.o *.elf *.hex
+
+install: all
+ $(AVRDUDE) -U flash:w:$(TARGET).hex:i
+
+##################################################
+# Dependency Targets
+##################################################
+fuses.hex: $(TARGET).elf
+ $(OBJCOPY) -j .fuse -O ihex $< $@ --change-section-lma .fuse=0
+
+lfuse.hex: fuses.hex
+ $(SREC) $< -Intel -crop 0x00 0x01 -offset 0x00 -O $@ -Intel
+
+hfuse.hex: fuses.hex
+ $(SREC) $< -Intel -crop 0x01 0x02 -offset -0x01 -O $@ -Intel
+
+$(TARGET).hex: $(TARGET).elf
+ $(OBJCOPY) -R .eeprom -R .fuse -O ihex $< $@
+
+$(TARGET).elf: $(SRCS:.c=.o)
+ $(CC) -mmcu=$(MMCU) $^ -o $@
+
+%.o: %.c $(HDRS) Makefile
+ $(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS)
diff --git a/firmware/e300/rev_b/PMC.atsln b/firmware/e300/rev_b/PMC.atsln
new file mode 100644
index 000000000..e9f4ffc79
--- /dev/null
+++ b/firmware/e300/rev_b/PMC.atsln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Atmel Studio Solution File, Format Version 11.00
+Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "PMC", "PMC.cproj", "{A379D421-4236-44AF-A711-52B7BDA29919}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|AVR = Debug|AVR
+ Release (DDR3L)|AVR = Release (DDR3L)|AVR
+ Release (DDR3L, Ti)|AVR = Release (DDR3L, Ti)|AVR
+ Release (Dev)|AVR = Release (Dev)|AVR
+ Release|AVR = Release|AVR
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.ActiveCfg = Debug|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.Build.0 = Debug|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.ActiveCfg = Release (DDR3L)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.Build.0 = Release (DDR3L)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.ActiveCfg = Release (DDR3L, Ti)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.Build.0 = Release (DDR3L, Ti)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.ActiveCfg = Release (Dev)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.Build.0 = Release (Dev)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.ActiveCfg = Release|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.Build.0 = Release|AVR
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/firmware/e300/rev_b/PMC.cproj b/firmware/e300/rev_b/PMC.cproj
new file mode 100644
index 000000000..e3784d10b
--- /dev/null
+++ b/firmware/e300/rev_b/PMC.cproj
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectVersion>6.0</ProjectVersion>
+ <ToolchainName>com.Atmel.AVRGCC8</ToolchainName>
+ <ProjectGuid>{a379d421-4236-44af-a711-52b7bda29919}</ProjectGuid>
+ <avrdevice>ATtiny88</avrdevice>
+ <avrdeviceseries>none</avrdeviceseries>
+ <OutputType>Executable</OutputType>
+ <Language>C</Language>
+ <OutputFileName>$(MSBuildProjectName)</OutputFileName>
+ <OutputFileExtension>.elf</OutputFileExtension>
+ <OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
+ <AssemblyName>PMC</AssemblyName>
+ <Name>PMC</Name>
+ <RootNamespace>PMC</RootNamespace>
+ <ToolchainFlavour>Native</ToolchainFlavour>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <OverrideVtorValue />
+ <eraseonlaunchrule>0</eraseonlaunchrule>
+ <AsfVersion>3.1.3</AsfVersion>
+ <avrtool>com.atmel.avrdbg.tool.stk600</avrtool>
+ <avrtoolinterface>ISP</avrtoolinterface>
+ <com_atmel_avrdbg_tool_stk600>
+ <ToolType>com.atmel.avrdbg.tool.stk600</ToolType>
+ <ToolName>STK600</ToolName>
+ <ToolNumber>004A8D68669B</ToolNumber>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <OverrideVtorValue>
+ </OverrideVtorValue>
+ <Channel>
+ <host>127.0.0.1</host>
+ <port>49172</port>
+ <ssl>False</ssl>
+ </Channel>
+ <ToolOptions>
+ <InterfaceName>ISP</InterfaceName>
+ <InterfaceProperties>
+ <JtagDbgClock>249000</JtagDbgClock>
+ <JtagProgClock>1000000</JtagProgClock>
+ <IspClock>25000</IspClock>
+ <JtagInChain>false</JtagInChain>
+ <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession>
+ <JtagDevicesBefore>0</JtagDevicesBefore>
+ <JtagDevicesAfter>0</JtagDevicesAfter>
+ <JtagInstrBitsBefore>0</JtagInstrBitsBefore>
+ <JtagInstrBitsAfter>0</JtagInstrBitsAfter>
+ </InterfaceProperties>
+ </ToolOptions>
+ </com_atmel_avrdbg_tool_stk600>
+ <com_atmel_avrdbg_tool_ispmk2>
+ <ToolType>com.atmel.avrdbg.tool.ispmk2</ToolType>
+ <ToolName>AVRISP mkII</ToolName>
+ <ToolNumber>000200136505</ToolNumber>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <OverrideVtorValue>
+ </OverrideVtorValue>
+ <Channel>
+ <host>127.0.0.1</host>
+ <port>49228</port>
+ <ssl>False</ssl>
+ </Channel>
+ <ToolOptions>
+ <InterfaceName>ISP</InterfaceName>
+ <InterfaceProperties>
+ <JtagDbgClock>249000</JtagDbgClock>
+ <JtagProgClock>1000000</JtagProgClock>
+ <IspClock>8000</IspClock>
+ <JtagInChain>false</JtagInChain>
+ <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession>
+ <JtagDevicesBefore>0</JtagDevicesBefore>
+ <JtagDevicesAfter>0</JtagDevicesAfter>
+ <JtagInstrBitsBefore>0</JtagInstrBitsBefore>
+ <JtagInstrBitsAfter>0</JtagInstrBitsAfter>
+ </InterfaceProperties>
+ </ToolOptions>
+ </com_atmel_avrdbg_tool_ispmk2>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK</Value>
+ <Value>ENABLE_SERIAL</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ </AvrGcc>
+ </ToolchainSettings>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>ATTINY88_DIP</Value>
+ <Value>DEBUG</Value>
+ <Value>I2C_REWORK</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ <avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
+ </AvrGcc>
+ </ToolchainSettings>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L)' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK</Value>
+ <Value>ENABLE_SERIAL</Value>
+ <Value>DDR3L</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ </AvrGcc>
+ </ToolchainSettings>
+ <OutputPath>bin\Release (DDR3)\</OutputPath>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release (Dev)' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK</Value>
+ <Value>ENABLE_SERIAL</Value>
+ <Value>ATTINY88_DIP</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection>True</avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ <avrgcc.linker.optimization.GarbageCollectUnusedSections>True</avrgcc.linker.optimization.GarbageCollectUnusedSections>
+ </AvrGcc>
+ </ToolchainSettings>
+ <OutputPath>bin\Release (Dev)\</OutputPath>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L, Ti)' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK_disabled</Value>
+ <Value>DDR3L_disabled_revB_R_FB_network_changed</Value>
+ <Value>CHARGER_TI</Value>
+ <Value>ENABLE_SERIAL_disabled</Value>
+ <Value>LED_POLARITY</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+</AvrGcc>
+ </ToolchainSettings>
+ <OutputPath>bin\Release (DDR3L, Ti)\</OutputPath>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="bq24190.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="bq24190.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="config.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="debug.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="debug.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="error.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="global.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="i2c.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="i2c.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="io.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="io.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc3675.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc3675.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc4155.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc4155.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="main.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="power.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="power.h">
+ <SubType>compile</SubType>
+ </Compile>
+ </ItemGroup>
+ <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
+</Project> \ No newline at end of file
diff --git a/firmware/e300/rev_b/bq24190.c b/firmware/e300/rev_b/bq24190.c
new file mode 100644
index 000000000..029beacf6
--- /dev/null
+++ b/firmware/e300/rev_b/bq24190.c
@@ -0,0 +1,335 @@
+/*
+ * bq24190.c
+ *
+ * Created: 11/12/2012 4:58:12 PM
+ * Author: Balint Seeber
+ */
+
+#ifdef CHARGER_TI
+
+#include "config.h"
+#include "bq24190.h"
+
+#include <util/delay.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+#ifndef I2C_REWORK
+#include "power.h"
+#endif // I2C_REWORK
+
+static io_pin_t USBPM_IRQ = IO_PB(1);
+
+#ifdef ATTINY88_DIP
+
+static io_pin_t CHRG_SDA = IO_PC(2);
+static io_pin_t CHRG_SCL = IO_PC(3);
+
+#else
+
+#ifdef I2C_REWORK
+
+static io_pin_t CHRG_SDA = IO_PC(4);
+static io_pin_t CHRG_SCL = IO_PC(5);
+
+#else
+
+#define CHRG_SDA PWR_SDA
+#define CHRG_SCL PWR_SCL
+
+#endif // I2C_REWORK
+
+#endif // ATTINY88_DIP
+
+const bool _bq24190_pull_up = false;
+
+#define BQ24190_BASE_ADDRESS (0x6B << 1)
+#define BQ24190_WRITE_ADDRESS (BQ24190_BASE_ADDRESS + 0)
+#define BQ24190_READ_ADDRESS (BQ24190_BASE_ADDRESS + 1)
+
+enum BQ24190Registers
+{
+ BQ24190_REG_INPUT_SOURCE_CTL= 0,
+ BQ24190_REG_PWR_ON_CONFIG = 1,
+ BQ24190_REG_CHARGE_CURRENT = 2,
+ BQ24190_REG_PRE_TERM_CURRENT= 3,
+ BQ24190_REG_CHARGE_VOLTAGE = 4,
+ BQ24190_REG_TIMER_CONTROL = 5,
+ BQ24190_REG_SYSTEM_STATUS = 8,
+ BQ24190_REG_FAULT = 9
+};
+/*
+enum BQ24190TimerControl
+{
+
+};
+*/
+enum BQ24190Shifts
+{
+ BQ24190_SHIFTS_CHARGER_CONFIG = 4,
+ BQ24190_SHIFTS_I2C_WATCHDOG = 4,
+ BQ24190_SHIFTS_CHARGER_STATUS = 4,
+ BQ24190_SHIFTS_CHARGER_FAULT = 4,
+};
+
+enum BQ24190VBusStatus
+{
+ BQ24190_VBUS_UNKNOWN,
+ BQ24190_VBUS_USB,
+ BQ24190_VBUS_ADAPTER,
+ BQ24190_VBUS_OTG
+};
+
+enum BQ24190ChargerStatus
+{
+ BQ24190_CHRG_STAT_NOT_CHARGING,
+ BQ24190_CHRG_STAT_PRE_CHARGE,
+ BQ24190_CHRG_STAT_FAST_CHARGING,
+ BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE,
+ BQ24190_CHRG_STAT_MASK = BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE
+};
+
+enum BQ24190SystemStatus
+{
+ BQ24190_STATUS_DPM = 0x08,
+ BQ24190_STATUS_POWER_GOOD = 0x04,
+ BQ24190_STATUS_THERMAL_REGULATION = 0x02,
+ BQ24190_STATUS_VSYSMIN_REGULATION = 0x01
+};
+
+enum BQ24190Faults
+{
+ BQ24190_FAULT_WATCHDOG_EXPIRED = 0x80,
+ BQ24190_FAULT_VBUS_OVERLOADED = 0x40,
+ BQ24190_FAULT_BATOVP = 0x08
+};
+
+enum BQ24190ChargerFaults
+{
+ BQ24190_CHRGFAULT_NORMAL,
+ BQ24190_CHRGFAULT_INPUT,
+ BQ24190_CHRGFAULT_THERMAL,
+ BQ24190_CHRGFAULT_SAFETY_TIMER
+};
+
+enum BQ24190NTCFaults
+{
+ BQ24190_NTCFAULT_NORMAL,
+ BQ24190_NTCFAULT_TS1_COLD,
+ BQ24190_NTCFAULT_TS1_HOT,
+ BQ24190_NTCFAULT_TS2_COLD,
+ BQ24190_NTCFAULT_TS2_HOT,
+ BQ24190_NTCFAULT_BOTH_COLD,
+ BQ24190_NTCFAULT_BOTH_HOT
+};
+
+bool bq24190_toggle_charger(bool on)
+{
+ uint8_t config = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQPC ", false);
+ debug_log_hex(config);
+
+ config &= ~(0x3 << BQ24190_SHIFTS_CHARGER_CONFIG);
+ if (on)
+ config |= (0x01 << BQ24190_SHIFTS_CHARGER_CONFIG); // Enable charger
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, config, _bq24190_pull_up) == false)
+ return false;
+
+ ////////
+/*
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQPC ", false);
+ debug_log_hex(config);
+*/
+ ////////
+
+ return true;
+}
+
+bool bq24190_init(bool disable_charger)
+{
+#ifdef I2C_REWORK
+ i2c_init_ex(CHRG_SDA, CHRG_SCL, _bq24190_pull_up);
+#endif // I2C_REWORK
+ io_input_pin(USBPM_IRQ);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP)
+ //io_set_pin(USBPM_IRQ); // [Enable pull-up for Open Drain] AVR pull-up not enough
+#endif // DEBUG
+ if (disable_charger)
+ {
+ if (bq24190_toggle_charger(false) == false)
+ return false;
+ }
+
+ ///////////////////////////////////
+
+ uint8_t timer_control = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_TIMER_CONTROL, &timer_control, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQTC ", false);
+ debug_log_hex(timer_control);
+
+ timer_control &= ~(0x3 << BQ24190_SHIFTS_I2C_WATCHDOG);
+ timer_control |= (0x00 << BQ24190_SHIFTS_I2C_WATCHDOG); // Disable I2C watch dog
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_TIMER_CONTROL, timer_control, _bq24190_pull_up) == false)
+ return false;
+
+ ///////////////////////////////////
+
+ //BQ24190_REG_PWR_ON_CONFIG
+ // Minimum System Voltage Limit: (default) 101 3.5V
+
+ //BQ24190_REG_CHARGE_CURRENT
+ // Fast Charge Current Limit: (default) 011000 2048mA
+
+ //BQ24190_REG_PRE_TERM_CURRENT
+ // Pre-charge current limit: (default) 0001 256mA
+ // Termination current limit: (default) 0001 256mA
+
+ //BQ24190_REG_CHARGE_VOLTAGE
+ // Charge voltage limit: (default) 101100 4.208V
+
+ ///////////////////////////////////
+
+ uint8_t input_src_ctl = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, &input_src_ctl, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQIS ", false);
+ debug_log_hex(input_src_ctl);
+
+ // Input voltage limit: (default) 0110 4.36V
+
+ //input_src_ctl &= ~(0x07);
+ input_src_ctl |= (0x07); // Set 3A limit
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, input_src_ctl, _bq24190_pull_up) == false)
+ return false;
+
+ return true;
+}
+
+bool bq24190_has_interrupt(void)
+{
+ //bool state = io_test_pin(USBPM_IRQ);
+ //debug_log_ex("BQIRQ", false);
+ //debug_log_byte(state);
+ //return (state != 1);
+ return (io_test_pin(USBPM_IRQ) == false);
+}
+
+static uint8_t _bq24190_last_status, _bq24190_last_fault;
+
+bool _bq24190_handle_irq(void)
+{
+ uint8_t val = 0x00;
+ bool result = false;
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ debug_log_ex("BQST ", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ _bq24190_last_status = val;
+
+ debug_log_ex("BQST ", false);
+ debug_log_hex(val);
+
+ /*if (val & LTC4155_WALLSNS_GOOD)
+ {
+ uint8_t wall_state = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ wall_state &= ~0x1E;
+ wall_state |= 0x0E;
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ debug_log("I+");
+ }*/
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ debug_log_ex("BQF ", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ _bq24190_last_fault = val;
+
+ debug_log_ex("BQF ", false);
+ debug_log_hex(val);
+
+ val = (_bq24190_last_status >> BQ24190_SHIFTS_CHARGER_STATUS) & BQ24190_CHRG_STAT_MASK;
+
+ if (_state.blink_error == BlinkError_None)
+ {
+ switch (val)
+ {
+ case BQ24190_CHRG_STAT_PRE_CHARGE:
+ case BQ24190_CHRG_STAT_FAST_CHARGING:
+ //case BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE:
+ {
+ if ((_state.battery_not_present == false)/* &&
+ (_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD))*/)
+ {
+ //charge_set_led(true);
+ charge_notify(true);
+ break;
+ }
+ }
+ //case BQ24190_CHRG_STAT_NOT_CHARGING:
+ default:
+ //charge_set_led(false);
+ charge_notify(false);
+ }
+ }
+
+// bq24190_dump();
+
+ result = true;
+_bq24190_handle_fail:
+ return result;
+}
+
+bool bq24190_handle_irq(void) // IRQ is pulsed (not held)
+{
+ pmc_mask_irqs(true);
+
+ //_delay_ms(250); // [Wait for registers to update]
+
+ bool result = _bq24190_handle_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+//void bq24190_dump(void)
+/*
+bool bq24190_set_charge_current_limit(uint8_t deciamps)
+{
+ return true;
+}
+*/
+#endif // CHARGER_TI
diff --git a/firmware/e300/rev_b/bq24190.h b/firmware/e300/rev_b/bq24190.h
new file mode 100644
index 000000000..dc20ca796
--- /dev/null
+++ b/firmware/e300/rev_b/bq24190.h
@@ -0,0 +1,26 @@
+/*
+ * bq24190.h
+ *
+ * Created: 11/12/2012 4:58:23 PM
+ * Author: Balint Seeber
+ */
+
+
+#ifndef BQ24190_H_
+#define BQ24190_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef CHARGER_TI
+
+bool bq24190_init(bool disable_charger);
+bool bq24190_has_interrupt(void);
+bool bq24190_handle_irq(void);
+//void bq24190_dump(void);
+//bool bq24190_set_charge_current_limit(uint8_t deciamps);
+bool bq24190_toggle_charger(bool on);
+
+#endif // !CHARGER_TI
+
+#endif /* BQ24190_H_ */
diff --git a/firmware/e300/rev_b/config.h b/firmware/e300/rev_b/config.h
new file mode 100644
index 000000000..2547f65f0
--- /dev/null
+++ b/firmware/e300/rev_b/config.h
@@ -0,0 +1,2 @@
+#define __DELAY_BACKWARD_COMPATIBLE__ // Avoid compile-time arg error to '__builtin_avr_delay_cycles'
+#define F_CPU 1000000UL // 1 MHz (8MHz / 8)
diff --git a/firmware/e300/rev_b/debug.c b/firmware/e300/rev_b/debug.c
new file mode 100644
index 000000000..ff7d09445
--- /dev/null
+++ b/firmware/e300/rev_b/debug.c
@@ -0,0 +1,297 @@
+/*
+ * debug.c
+ */
+
+#include "config.h"
+#include "debug.h"
+
+#include <util/delay.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "io.h"
+#include "power.h"
+#include "global.h"
+
+#define DEBUG_BLINK_DELAY 250 // ms
+
+#ifdef ATTINY88_DIP
+
+#define SERIAL_DEBUG_INDEX 6
+#define SERIAL_DEBUG_PORT PORTD
+static io_pin_t SERIAL_DEBUG = IO_PD(SERIAL_DEBUG_INDEX);
+
+#else
+/*
+#ifdef I2C_REWORK
+//static io_pin_t SERIAL_DEBUG = IO_PC(1); // EN1
+#else
+//static io_pin_t SERIAL_DEBUG = EN4;
+#endif // I2C_REWORK
+*/
+// No good: PWR_EN4 trace still connected to LTC3675
+//#define SERIAL_DEBUG_INDEX 1
+//#define SERIAL_DEBUG_PORT PORTA
+//static io_pin_t SERIAL_DEBUG = IO_PA(SERIAL_DEBUG_INDEX);
+
+// AVR_MISO
+#define SERIAL_DEBUG_INDEX 4
+#define SERIAL_DEBUG_PORT PORTB
+static io_pin_t SERIAL_DEBUG = IO_PB(SERIAL_DEBUG_INDEX);
+
+#endif // ATTINY88_DIP
+/*
+#ifdef DEBUG
+
+#else
+
+#endif // DEBUG
+*/
+#ifdef DEBUG
+
+#ifdef ATTINY88_DIP
+static io_pin_t DEBUG_1 = IO_PB(6);
+static io_pin_t DEBUG_2 = IO_PB(7);
+#endif // ATTINY88_DIP
+
+void debug_init()
+{
+ io_output_pin(DEBUG_1);
+ io_output_pin(DEBUG_2);
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+#ifdef ENABLE_SERIAL
+ io_set_pin(SERIAL_DEBUG);
+ io_output_pin(SERIAL_DEBUG);
+#endif // ENABLE_SERIAL
+}
+
+#else
+
+void debug_init()
+{
+#ifdef ENABLE_SERIAL
+ io_set_pin(SERIAL_DEBUG);
+ io_output_pin(SERIAL_DEBUG);
+#endif // ENABLE_SERIAL
+}
+
+#endif // DEBUG
+
+#if defined(DEBUG) && !defined(DEBUG_VOID)
+
+void debug_set(io_pin_t pin, bool enable)
+{
+ io_enable_pin(pin, !enable);
+}
+
+void debug_blink(uint8_t count)
+{
+ io_enable_pin(DEBUG_1, false);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+
+ for (; count > 0; count--) {
+ io_enable_pin(DEBUG_2, false);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ }
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+}
+
+void debug_blink_rev(uint8_t count)
+{
+ io_enable_pin(DEBUG_2, false);
+ io_enable_pin(DEBUG_1, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+
+ for (; count > 0; count--) {
+ io_enable_pin(DEBUG_1, false);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ io_enable_pin(DEBUG_1, true);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ }
+
+ io_enable_pin(DEBUG_2, true);
+ io_enable_pin(DEBUG_1, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+}
+
+void debug_blink2(uint8_t count)
+{
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+
+ bool b = false;
+ for (; count > 0; count--) {
+ io_enable_pin(DEBUG_1, b);
+ io_enable_pin(DEBUG_2, b);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ b = !b;
+ }
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+}
+
+void debug_wait(void)
+{
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+
+ bool b = false;
+ while (true)
+ {
+ io_enable_pin(DEBUG_1, b);
+ io_enable_pin(DEBUG_2, !b);
+
+ _delay_ms(DEBUG_BLINK_DELAY);
+
+ b = !b;
+ }
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+}
+
+#else
+
+#ifndef DEBUG_VOID
+
+void debug_blink_rev(uint8_t count)
+{
+ charge_set_led(true);
+ _delay_ms(DEBUG_BLINK_DELAY * 4);
+
+ for (; count > 0; count--) {
+ charge_set_led(false);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ charge_set_led(true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+ }
+
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+ charge_set_led(false);
+ _delay_ms(DEBUG_BLINK_DELAY * 4);
+}
+#endif // DEBUG_VOID
+
+#endif // DEBUG
+
+#ifdef ENABLE_SERIAL
+
+static void _serial_tx(uint8_t* buffer)
+{
+ //uint8_t time_fix = 0;
+ // 3333/2 - 10
+ // 650
+ // [-2 for DEV] -20 works (perhaps different USB-Serial converter)
+ // [-20 for PRD] Which board?
+ // +20 Board #5 (-0: 3.592, -10: 3.280)
+ const uint16_t delay = 650+20;
+ uint16_t countdown;
+
+ for (uint8_t j = 0; j < 10; ++j)
+ {
+ if (buffer[j])
+ SERIAL_DEBUG_PORT |= _BV(SERIAL_DEBUG_INDEX);
+ else
+ SERIAL_DEBUG_PORT &= ~_BV(SERIAL_DEBUG_INDEX);
+
+ countdown = delay;
+ while (--countdown)
+ __asm("nop");
+ }
+}
+
+static void _serial_tx_char(char c)
+{
+ uint8_t buffer[10];
+ uint8_t i = 0;
+
+ buffer[i++] = 0; // START
+ for (int idx = 0; idx < 8; ++idx)
+ buffer[i++] = (((uint8_t)(c) & ((uint8_t)1<<((idx)))) ? 0x01 : 0x00); // Endianness: 7-
+ buffer[i++] = 1; // STOP
+
+ _serial_tx(buffer);
+}
+
+void debug_log_ex_P(const char* message, bool new_line)
+{
+ char c = pgm_read_byte(message);
+ if (c == '\0')
+ return;
+
+ pmc_mask_irqs(true);
+
+ do
+ {
+ _serial_tx_char(c);
+ c = pgm_read_byte(++message);
+ } while (c != '\0');
+
+ if (new_line)
+ _serial_tx_char('\n');
+
+ io_set_pin(SERIAL_DEBUG);
+
+ pmc_mask_irqs(false);
+}
+
+void _debug_log_ex(const char* message, bool new_line)
+{
+ if (message[0] == '\0')
+ return;
+
+ pmc_mask_irqs(true);
+
+ do
+ {
+ _serial_tx_char(*message);
+ } while (*(++message) != '\0');
+
+ if (new_line)
+ _serial_tx_char('\n');
+
+ io_set_pin(SERIAL_DEBUG);
+
+ pmc_mask_irqs(false);
+}
+
+void debug_log_byte_ex(uint8_t n, bool new_line)
+{
+ char ch[4];
+ ch[0] = '0' + (n / 100);
+ ch[1] = '0' + ((n % 100) / 10);
+ ch[2] = '0' + (n % 10);
+ ch[3] = '\0';
+ _debug_log_ex(ch, new_line);
+}
+
+void debug_log_hex_ex(uint8_t n, bool new_line)
+{
+ char ch[4];
+ ch[0] = 'x';
+ uint8_t _n = n >> 4;
+ if (_n < 10)
+ ch[1] = '0' + _n;
+ else
+ ch[1] = 'A' + (_n - 10);
+ n &= 0x0F;
+ if (n < 10)
+ ch[2] = '0' + n;
+ else
+ ch[2] = 'A' + (n - 10);
+ ch[3] = '\0';
+ _debug_log_ex(ch, new_line);
+}
+
+#endif // ENABLE_SERIAL
diff --git a/firmware/e300/rev_b/debug.h b/firmware/e300/rev_b/debug.h
new file mode 100644
index 000000000..5e6435972
--- /dev/null
+++ b/firmware/e300/rev_b/debug.h
@@ -0,0 +1,90 @@
+/*
+ * debug.h
+ */
+
+#ifndef DEBUG_H_
+#define DEBUG_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <avr/pgmspace.h>
+
+#include "io.h"
+
+#ifdef DEBUG
+#define DEBUG_INLINE
+#define DEBUG_NOOP ;
+#define LED_ON false
+#define LED_OFF true
+#else
+#define DEBUG_INLINE inline
+#define DEBUG_NOOP {}
+#define LED_ON true
+#define LED_OFF false
+#endif // DEBUG
+
+//#define DEBUG_VOID
+#define DEBUG_SAFETY
+
+#ifdef DEBUG_VOID
+
+//#define debug_init (void)
+#define debug_set (void)
+#define debug_blink (void)
+#define debug_blink_rev (void)
+#define debug_blink2 (void)
+#define debug_wait (void)
+
+#else
+
+//DEBUG_INLINE void debug_init(void) DEBUG_NOOP
+DEBUG_INLINE void debug_set(io_pin_t pin, bool enable) DEBUG_NOOP
+DEBUG_INLINE void debug_blink(uint8_t count) DEBUG_NOOP
+//DEBUG_INLINE void debug_blink_rev(uint8_t count) DEBUG_NOOP
+void debug_blink_rev(uint8_t count);
+DEBUG_INLINE void debug_blink2(uint8_t count) DEBUG_NOOP
+DEBUG_INLINE void debug_wait(void) DEBUG_NOOP
+
+#endif // DEBUG_VOID
+
+#if defined(DEBUG) && !defined(ENABLE_SERIAL)
+#define ENABLE_SERIAL
+#endif // DEBUG && !ENABLE_SERIAL
+
+/*DEBUG_INLINE */void debug_init(void)/* DEBUG_NOOP*/;
+
+#ifdef ENABLE_SERIAL
+
+void debug_log_ex_P(const char* message, bool new_line);
+void debug_log_hex_ex(uint8_t n, bool new_line);
+void debug_log_byte_ex(uint8_t n, bool new_line);
+void _debug_log_ex(const char* message, bool new_line);
+
+// Prototypes to silence avr-gcc
+inline void debug_log_P(const char* message);
+inline void debug_log_hex(uint8_t n);
+inline void debug_log_byte(uint8_t n);
+inline void _debug_log(const char* message);
+
+inline void debug_log_P(const char* message) { debug_log_ex_P(message, true); }
+inline void debug_log_hex(uint8_t n) { debug_log_hex_ex(n, true); }
+inline void debug_log_byte(uint8_t n) { debug_log_byte_ex(n, true); }
+inline void _debug_log(const char* message) { _debug_log_ex(message, true); }
+
+#else
+
+inline void debug_log_ex_P (const char* message, bool new_line) {};
+inline void debug_log_hex_ex (uint8_t n, bool new_line) {};
+inline void debug_log_byte_ex (uint8_t n, bool new_line) {};
+inline void _debug_log_ex (const char* message, bool new_line) {};
+
+#define debug_log_P (void)
+#define debug_log_hex (void)
+#define debug_log_byte (void)
+#define _debug_log (void)
+#endif // ENABLE_SERIAL
+
+#define debug_log(x) debug_log_P(PSTR(x))
+#define debug_log_ex(x,nl) debug_log_ex_P(PSTR(x), nl)
+
+#endif /* DEBUG_H_ */
diff --git a/firmware/e300/rev_b/error.h b/firmware/e300/rev_b/error.h
new file mode 100644
index 000000000..82a8f0aca
--- /dev/null
+++ b/firmware/e300/rev_b/error.h
@@ -0,0 +1,31 @@
+/*
+ * error.h
+ *
+ * Created: 4/09/2012 6:25:53 PM
+ * Author: Balint Seeber
+ */
+
+
+#ifndef ERROR_H_
+#define ERROR_H_
+
+enum ErrorBlinkCount // Lower number = higher priority
+{
+ BlinkError_None,
+ // Low power/battery
+ BlinkError_LowVoltage,
+ BlinkError_LTC3675_UnderVoltage = BlinkError_LowVoltage,
+ BlinkError_LTC4155_UnderVoltage = BlinkError_LowVoltage, // FIXME: This does not work when checking status
+ // Should match power boot steps
+ BlinkError_FPGA_Power,
+ BlinkError_DRAM_Power,
+ BlinkError_1_8V_Peripherals_Power,
+ BlinkError_3_3V_Peripherals_Power,
+ BlinkError_TX_Power,
+ // LTC3675
+ BlinkError_LTC3675_OverTemperature,
+ // LTC4155
+ BlinkError_LTC4155_BadCell
+};
+
+#endif /* ERROR_H_ */
diff --git a/firmware/e300/rev_b/global.h b/firmware/e300/rev_b/global.h
new file mode 100644
index 000000000..50fab581d
--- /dev/null
+++ b/firmware/e300/rev_b/global.h
@@ -0,0 +1,49 @@
+/*
+ * global.h
+ *
+ * Created: 31/08/2012 8:47:14 PM
+ * Author: Balint Seeber
+ */
+
+#ifndef GLOBAL_H_
+#define GLOBAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+typedef struct State
+{
+ bool interrupts_enabled;
+ uint8_t interrupt_depth;
+ //bool timers_running;
+ uint8_t active_timers;
+ bool powered;
+ bool battery_not_present;
+ bool battery_charging;
+ bool wake_up;
+ bool power_off;
+ bool core_power_bad;
+ bool ltc3675_irq;
+#ifdef CHARGER_TI
+ bool bq24190_irq;
+#else
+ bool ltc4155_irq;
+#endif // CHARGER_TI
+ //bool low_battery;
+ uint8_t blink_error;
+ uint8_t blinker_state;
+ uint8_t blink_loops;
+ uint8_t blink_last_loop;
+ bool blink_stop;
+} STATE;
+
+//extern volatile bool _timers_running;
+extern volatile STATE _state;
+
+void pmc_set_blink_error(uint8_t count);
+uint8_t pmc_get_blink_error(void);
+
+bool pmc_mask_irqs(bool mask);
+
+#endif /* GLOBAL_H_ */
diff --git a/firmware/e300/rev_b/i2c.c b/firmware/e300/rev_b/i2c.c
new file mode 100644
index 000000000..70a28e61a
--- /dev/null
+++ b/firmware/e300/rev_b/i2c.c
@@ -0,0 +1,518 @@
+#include "config.h"
+#include "i2c.h"
+
+#include <util/delay.h>
+
+#include "io.h"
+#include "debug.h"
+
+/*
+ - Reset bus on failure (lack of ACK, etc)
+ - Clock stretching
+ - In pull-up mode, much code was commented out to ever avoid driving the bus (for a fleeting moment) as this was visible on the scope as short peaks (instead the line will briefly go Hi Z).
+*/
+
+volatile bool _i2c_disable_ack_check = false;
+
+// FIXME: Follow magic numbers should be in a struct that is passed into each function
+
+#define I2C_DEFAULT_RETRY_DELAY 1 // us MAGIC
+#define I2C_DEFAULT_MAX_ACK_RETRIES 10 // * I2C_DEFAULT_RETRY_DELAY us
+
+#define I2C_DEFAULT_BUS_WAIT 10 // us MAGIC
+#define I2C_DEFAULT_MAX_BUS_RETRIES 10
+
+#define I2C_DEFAULT_SCL_LOW_PERIOD 2 // 1.3 us
+#define I2C_DEFAULT_SCL_HIGH_PERIOD 1 // 0.6 us
+#define I2C_DEFAULT_BUS_FREE_TIME 2 // 1.3 us
+#define I2C_DEFAULT_STOP_TIME 1 // 0.6 us
+
+#define I2C_DELAY _delay_us // _delay_ms
+
+static bool _i2c_start_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+ // Assumes: SDA/SCL are both inputs
+
+ uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while ((io_test_pin(sda) == false) || (io_test_pin(scl) == false))
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (retries-- == 0)
+ {
+debug_log("I2C:S1");
+ return false;
+ }
+ }
+
+ // START condition
+// if (pull_up == false)
+ io_clear_pin(sda); // Set LOW before switching to output
+ io_output_pin(sda);
+// if (pull_up)
+// io_clear_pin(sda);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); // Thd, sta
+
+ retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while (io_test_pin(scl) == false) // SCL should remain high
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (retries-- == 0)
+ {
+ io_input_pin(sda);
+debug_log_ex("I2C:S2", false);
+debug_log_hex(scl);
+ return false;
+ }
+ }
+
+// if (pull_up == false)
+ io_clear_pin(scl);
+ io_output_pin(scl);
+// if (pull_up)
+// io_clear_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD / 2); // MAGIC
+
+ return true;
+}
+
+static bool _i2c_stop_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+ // Assumes:
+ // SCL is output & LOW
+ // SDA is input (Hi-Z, or pull-up enabled)
+
+ // Assuming pull-up already enabled
+ //if (pull_up)
+ // io_set_pin(sda);
+
+ bool result = true;
+
+ // SDA should be HIGH after ACK has been clocked away
+// bool skip_drive = false;
+ uint8_t retries = 0;
+ while (io_test_pin(sda) == false)
+ {
+ if (retries == I2C_DEFAULT_MAX_ACK_RETRIES)
+ {
+ debug_log_ex("I2C:STP ", false);
+ debug_log_hex(sda);
+ debug_blink_rev(4);
+
+// skip_drive = true;
+ result = false;
+ break; // SDA is being held low?!
+ }
+
+ ++retries;
+ I2C_DELAY(I2C_DEFAULT_RETRY_DELAY);
+ }
+
+ // STOP condition
+// if ((pull_up == false) || (skip_drive))
+ io_clear_pin(sda); // Don't tri-state if internal pull-up is used
+// //else
+// // Pin will now be driven, but having checked SDA is HIGH above means slave's SDA should be Open Collector (i.e. it won't blow up)
+ io_output_pin(sda); // Drive LOW
+// if (pull_up)
+// io_clear_pin(sda);
+
+ ///////////////////////////////////
+
+// if (pull_up)
+// io_set_pin(scl); // Don't tri-state if internal pull-up is used. Line will be driven, but assuming this is the only master on the clock line (i.e. no one else will pull it low).
+ io_input_pin(scl);
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_STOP_TIME);
+
+ ///////////////////////////////////
+
+// if ((pull_up) && (skip_drive == false))
+// io_set_pin(sda); // Don't tri-state if internal pull-up is used
+ io_input_pin(sda);
+// if ((pull_up) && (skip_drive))
+ io_set_pin(sda);
+ I2C_DELAY(I2C_DEFAULT_BUS_FREE_TIME);
+
+ return result;
+}
+/*
+static void _i2c_stop(io_pin_t sda, io_pin_t scl)
+{
+ _i2c_stop_ex(sda, scl, false);
+}
+*//*
+static void _i2c_abort_safe_ex(io_pin_t pin, bool pull_up)
+{
+ if (io_is_output(pin))
+ {
+ if (io_is_pin_set(pin)) // This is bad - hope no slave is pulling down the line
+ {
+ io_input_pin(pin); // Pull-up already enabled
+
+ if (pull_up == false)
+ io_clear_pin(pin); // Doing this after changing direction ensures the line is not brought down
+ }
+ else // Currently pulling line down
+ {
+ io_input_pin(pin); // Hi-Z
+
+ if (pull_up) // There will be a moment where the line will float (better than driving the line though...)
+ {
+ io_set_pin(pin);
+ }
+ }
+ }
+ else // Already an input
+ {
+ if (pull_up)
+ {
+ io_set_pin(pin); // Enable pull-ups
+ }
+ else
+ {
+ io_clear_pin(pin); // Disable pull-ups
+ }
+ }
+
+ // Normally: pin will be Hi-Z input
+ // With internal pull-up: pin will be input with pull-up enabled
+}
+*/
+static void _i2c_abort_safe(io_pin_t pin, bool pull_up)
+{
+ if (pull_up == false)
+ io_clear_pin(pin); // Should never be output/HIGH, could be input/<was outputting HIGH> so disable pull-ups
+
+ io_input_pin(pin);
+
+ if (pull_up)
+ io_set_pin(pin); // Enable pull-up
+}
+
+static void _i2c_abort_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+/* if (pull_up == false)
+ {
+ io_clear_pin(sda);
+ io_clear_pin(scl);
+ }
+
+ io_input_pin(scl);
+ io_input_pin(sda);
+
+ if (pull_up)
+ {
+ io_set_pin(sda);
+ io_set_pin(scl);
+ }
+*/
+ _i2c_abort_safe(scl, pull_up);
+ _i2c_abort_safe(sda, pull_up);
+
+ //_i2c_abort_safe_ex(scl, pull_up);
+ //_i2c_abort_safe_ex(sda, pull_up);
+}
+/*
+static void _i2c_abort(io_pin_t sda, io_pin_t scl)
+{
+ _i2c_abort_ex(sda, scl, false);
+}
+*/
+static bool _i2c_write_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t value, bool pull_up)
+{
+ // Assumes:
+ // SDA output is LOW
+ // SCL output is LOW
+
+ for (uint8_t i = 0; i < 8; ++i)
+ {
+ bool b = ((value & (0x01 << (7 - i))) != 0x00); // MSB first
+
+ if (b)
+ {
+ if (pull_up)
+ {
+// io_set_pin(sda); // This is bad (will drive line for a moment), but more stable than letting line float
+ io_input_pin(sda);
+ io_set_pin(sda);
+ }
+ else
+ io_input_pin(sda); // Release HIGH
+
+ if (io_test_pin(sda) == false)
+ {
+ debug_log("I2C:WR ");
+ debug_log_hex(sda);
+ debug_blink_rev(1);
+ return false;
+ }
+ }
+ else
+ {
+ if (pull_up)
+ {
+// if (io_is_output(sda))
+ io_clear_pin(sda);
+// else
+// {
+ io_output_pin(sda); // [This is bad (will drive line for a moment), but more stable than letting line float]
+// io_clear_pin(sda);
+// }
+ }
+ else
+ {
+ io_enable_pin(sda, false);
+ io_output_pin(sda); // Drive LOW
+ }
+ }
+
+ ///////////////////////////////
+
+ io_input_pin(scl); // Release HIGH
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+#ifdef I2C_ALLOW_CLOCK_STRETCH
+ uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while (io_test_pin(scl) == false) // Clock stretch requested?
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (--retries == 0)
+ {
+ io_input_pin(sda); // Release HIGH
+ if (pull_up)
+ io_set_pin(sda);
+
+ debug_log_ex("I2C:STRTCH ", false);
+ debug_log_hex(scl);
+ debug_blink_rev(2);
+ return false;
+ }
+ }
+#endif // I2C_ALLOW_CLOCK_STRETCH
+ if (pull_up)
+ io_clear_pin(scl);
+ io_output_pin(scl); // Drive LOW
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);
+ }
+
+ io_input_pin(sda); // Release HIGH
+ if (pull_up)
+ io_set_pin(sda); // Assuming letting line float won't confuse slave when pulling line LOW for ACK
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+
+ uint8_t retries = 0;
+ while ((_i2c_disable_ack_check == false) && (io_test_pin(sda)))
+ {
+ if (retries == I2C_DEFAULT_MAX_ACK_RETRIES)
+ {
+ debug_log_ex("I2C:ACK ", false);
+ debug_log_hex_ex(sda, false);
+ debug_log_hex(value);
+ debug_blink_rev(3);
+ return false; // Will abort and not release bus - done by caller
+ }
+
+ ++retries;
+ I2C_DELAY(I2C_DEFAULT_RETRY_DELAY);
+ }
+
+ // Clock away acknowledge
+// if (pull_up)
+// io_set_pin(scl);
+ io_input_pin(scl); // Release HIGH
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+
+ if (pull_up)
+ io_clear_pin(scl);
+ io_output_pin(scl); // Drive LOW
+// if (pull_up)
+// io_clear_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);
+
+ return true;
+}
+
+static bool _i2c_read_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t* value, bool pull_up)
+{
+ // Assumes:
+ // SDA output is LOW
+ // SCL output is LOW
+
+ io_input_pin(sda);
+ if (pull_up)
+ io_set_pin(sda); // OK to leave line floating for a moment (better not to drive as slave will be pulling it to ground)
+
+ (*value) = 0x00;
+
+ for (uint8_t i = 0; i < 8; ++i)
+ {
+// if (pull_up)
+// io_set_pin(scl); // [Not ideal with pull-up]
+ io_input_pin(scl); // Release HIGH
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+#ifdef I2C_ALLOW_CLOCK_STRETCH
+ uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while (io_test_pin(scl) == false) // Clock stretch requested?
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (--retries == 0)
+ {
+ debug_log_ex("I2C:R ");
+ debug_log_hex(scl);
+ debug_blink_rev(5);
+ return false;
+ }
+ }
+#endif // I2C_ALLOW_CLOCK_STRETCH
+ (*value) |= ((io_test_pin(sda) ? 0x1 : 0x0) << (7 - i)); // MSB first
+
+ if (pull_up)
+ io_clear_pin(scl);
+ io_output_pin(scl); // Drive LOW (not ideal with pull-up)
+// if (pull_up)
+// io_clear_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);
+ }
+
+ // Not necessary to ACK since it's only this one byte
+
+ return true;
+}
+
+bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up)
+{
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ return false;
+
+ if (_i2c_write_byte_ex(sda, scl, addr & ~0x01, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R21:", false);
+ debug_log("R21");
+ //debug_log_hex(addr);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R22:", false);
+ debug_log("R22");
+ //debug_log_hex(subaddr);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ io_input_pin(scl);
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ {
+ return false;
+ }
+
+ if (_i2c_write_byte_ex(sda, scl, addr | 0x01, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R23:", false);
+ debug_log("R23");
+ //debug_log_hex(addr);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R24:", false);
+ debug_log("R24");
+ //debug_log_hex(*value);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ if (_i2c_stop_ex(sda, scl, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ debug_log("R25");
+#endif // I2C_EXTRA_DEBUGGING
+ }
+
+ return true;
+i2c_read2_fail:
+ _i2c_abort_ex(sda, scl, pull_up);
+ return false;
+}
+
+bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up)
+{
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ return false;
+
+ if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false)
+ goto i2c_write_fail;
+
+ if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false)
+ goto i2c_write_fail;
+
+ if (_i2c_write_byte_ex(sda, scl, value, pull_up) == false)
+ goto i2c_write_fail;
+
+ _i2c_stop_ex(sda, scl, pull_up);
+
+ return true;
+i2c_write_fail:
+ _i2c_abort_ex(sda, scl, pull_up);
+ return false;
+}
+
+bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value)
+{
+ return i2c_write_ex(sda, scl, addr, subaddr, value, false);
+}
+
+bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up)
+{
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ return false;
+
+ if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false)
+ goto i2c_read_fail;
+
+ if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false)
+ goto i2c_read_fail;
+
+ if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false)
+ goto i2c_read_fail;
+
+ _i2c_stop_ex(sda, scl, pull_up);
+
+ return true;
+i2c_read_fail:
+ _i2c_abort_ex(sda, scl, pull_up);
+ return false;
+}
+
+bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value)
+{
+ return i2c_read_ex(sda, scl, addr, subaddr, value, false);
+}
+
+void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+ _i2c_abort_ex(sda, scl, pull_up);
+}
+
+void i2c_init(io_pin_t sda, io_pin_t scl)
+{
+ i2c_init_ex(sda, scl, false);
+}
diff --git a/firmware/e300/rev_b/i2c.h b/firmware/e300/rev_b/i2c.h
new file mode 100644
index 000000000..5898e7e43
--- /dev/null
+++ b/firmware/e300/rev_b/i2c.h
@@ -0,0 +1,17 @@
+#ifndef I2C_H
+#define I2C_H
+
+#include "io.h"
+
+void i2c_init(io_pin_t sda, io_pin_t scl);
+bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value);
+bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value);
+
+void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up);
+bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up);
+bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up);
+bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up);
+
+extern volatile bool _i2c_disable_ack_check;
+
+#endif // I2C_H
diff --git a/firmware/e300/rev_b/io.c b/firmware/e300/rev_b/io.c
new file mode 100644
index 000000000..0256181c6
--- /dev/null
+++ b/firmware/e300/rev_b/io.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009-2012 Ettus Research LLC
+ */
+
+#include "io.h"
+#include <avr/io.h>
+
+#define _GET_PIN(pin) ((pin) & 0xf)
+#define _GET_MASK(pin) (_BV(_GET_PIN(pin)))
+#define _GET_REG(pin, reg_x) (*reg_x[pin >> 4])
+
+#ifndef IO_DEBUG
+static volatile uint8_t *ddr_x[] = {&DDRA, &DDRB, &DDRC, &DDRD}; // 0: input, 1: output
+static volatile uint8_t *port_x[] = {&PORTA, &PORTB, &PORTC, &PORTD}; // Port contents (will appear at output if direction is set to output. If input, '1' enables pull-ups, '0' set tri-state)
+static volatile uint8_t *pin_x[] = {&PINA, &PINB, &PINC, &PIND}; // Port contents (input) If output, will return value on PORT
+#endif
+
+void io_output_pin(io_pin_t pin){
+#ifndef IO_DEBUG
+ _GET_REG(pin, ddr_x) |= _GET_MASK(pin);
+#endif
+}
+
+void io_input_pin(io_pin_t pin){
+#ifndef IO_DEBUG
+ _GET_REG(pin, ddr_x) &= ~_GET_MASK(pin);
+#endif
+}
+
+bool io_is_output(io_pin_t pin){
+#ifndef IO_DEBUG
+ return bit_is_set(_GET_REG(pin, ddr_x), _GET_PIN(pin));
+#else
+ return 0;
+#endif
+}
+
+bool io_is_input(io_pin_t pin){
+ return !io_is_output(pin);
+}
+
+void io_set_pin(io_pin_t pin){ // In input mode, will enable pull-ups
+#ifndef IO_DEBUG
+ _GET_REG(pin, port_x) |= _GET_MASK(pin);
+#endif
+}
+
+void io_clear_pin(io_pin_t pin){ // In input mode, will disable pull-ups
+#ifndef IO_DEBUG
+ _GET_REG(pin, port_x) &= ~_GET_MASK(pin);
+#endif
+}
+
+bool io_is_pin_set(io_pin_t pin){
+#ifndef IO_DEBUG
+ return bit_is_set(_GET_REG(pin, port_x), _GET_PIN(pin));
+#else
+ return 0;
+#endif
+}
+
+void io_enable_pin(io_pin_t pin, bool enable){
+ if (enable)
+ io_set_pin(pin);
+ else
+ io_clear_pin(pin);
+}
+
+bool io_test_pin(io_pin_t pin){
+#ifndef IO_DEBUG
+ return bit_is_set(_GET_REG(pin, pin_x), _GET_PIN(pin));
+#else
+ return 0;
+#endif
+}
diff --git a/firmware/e300/rev_b/io.h b/firmware/e300/rev_b/io.h
new file mode 100644
index 000000000..7eea8f0a3
--- /dev/null
+++ b/firmware/e300/rev_b/io.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2009 Ettus Research LLC
+ */
+
+#ifndef IO_H
+#define IO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define IO_PX(port, pin) ((uint8_t)(((port - 'A') << 4) + pin))
+#define IO_PA(pin) IO_PX('A', pin)
+#define IO_PB(pin) IO_PX('B', pin)
+#define IO_PC(pin) IO_PX('C', pin)
+#define IO_PD(pin) IO_PX('D', pin)
+
+typedef const uint8_t io_pin_t;
+
+void io_output_pin(io_pin_t pin);
+void io_input_pin(io_pin_t pin);
+bool io_is_output(io_pin_t pin);
+bool io_is_input(io_pin_t pin);
+
+void io_set_pin(io_pin_t pin);
+void io_clear_pin(io_pin_t pin);
+void io_enable_pin(io_pin_t pin, bool enable);
+bool io_is_pin_set(io_pin_t pin);
+
+bool io_test_pin(io_pin_t pin);
+
+#endif /* IO_H */
diff --git a/firmware/e300/rev_b/ltc3675.c b/firmware/e300/rev_b/ltc3675.c
new file mode 100644
index 000000000..0f85ec7e5
--- /dev/null
+++ b/firmware/e300/rev_b/ltc3675.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2012 Ettus Research LLC
+ */
+
+/*
+ ? STOP condition after writing address on read
+ - Default buck/boost register values are OK
+*/
+
+#include "config.h"
+#include "ltc3675.h"
+
+//#include <stdio.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+#ifndef I2C_REWORK
+#include "power.h"
+#endif // I2C_REWORK
+
+const bool _ltc3675_pull_up =
+#ifdef I2C_REWORK
+ true
+#else
+ false
+#endif // I2C_REWORK
+;
+
+volatile ltc3675_reg_helper_fn _ltc3675_reg_helper;
+
+//#define HARDWIRE_ENABLE // Use hardware enable pins instead of I2C on regulators that support it
+
+#ifdef ATTINY88_DIP
+
+#ifdef HARDWIRE_ENABLE
+static io_pin_t PWR_EN1 = IO_PC(7); // Not routed by card
+static io_pin_t PWR_EN2 = IO_PA(0); // Not available on DIP
+static io_pin_t PWR_EN3 = IO_PA(1); // Not available on DIP
+static io_pin_t PWR_EN4 = IO_PB(6); // Instead of FTDI_BCD
+static io_pin_t PWR_EN5 = IO_PB(7); // Instead of FTDI_PWREN2
+#endif // HARDWIRE_ENABLE
+
+//static io_pin_t PWR_SDA = IO_PC(4);
+//static io_pin_t PWR_SCL = IO_PC(5);
+
+#else
+
+#ifdef HARDWIRE_ENABLE
+static io_pin_t PWR_EN1 = IO_PC(1);
+//static io_pin_t PWR_EN2 = IO_PC(2); // Now used by I2C for charge controller
+//static io_pin_t PWR_EN3 = IO_PC(3); // Now used by I2C for charge controller
+static io_pin_t PWR_EN4 = IO_PA(1);
+static io_pin_t PWR_EN5 = IO_PA(2);
+#endif // HARDWIRE_ENABLE
+
+#ifdef I2C_REWORK
+static io_pin_t PWR_SDA = IO_PC(2); // Instead of EN5
+static io_pin_t PWR_SCL = IO_PA(2); // Instead of EN2
+#endif // I2C_REWORK
+
+#endif // ATTINY88_DIP
+
+static io_pin_t PWR_IRQ = IO_PD(0);
+static io_pin_t WAKEUP = IO_PD(2);
+static io_pin_t ONSWITCH_DB = IO_PD(3);
+static io_pin_t PWR_RESET = IO_PD(4);
+
+#define LTC3675_BASE_ADDRESS 0x12
+#define LTC3675_WRITE_ADDRESS (LTC3675_BASE_ADDRESS + 0)
+#define LTC3675_READ_ADDRESS (LTC3675_BASE_ADDRESS + 1)
+
+#define LTC3675_RETRY_DELAY 1 // us MAGIC
+#define LTC3675_MAX_ACK_RETRIES 10 // * LTC3675_RETRY_DELAY us
+
+#define LTC3675_SCL_LOW_PERIOD 2 // 1.3 us
+#define LTC3675_SCL_HIGH_PERIOD 1 // 0.6 us
+#define LTC3675_BUS_FREE_TIME 2 // 1.3 us
+#define LTC3675_STOP_TIME 1 // 0.6 us
+
+#define LTC3675_REGULATOR_ENABLE_DELAY 10 // 50 // ms (some arbitrary value so that the external power supply can settle)
+
+enum LTC3675Registers
+{
+ LTC3675_REG_NONE = 0x00,
+ LTC3675_REG_BUCK1 = 0x01,
+ LTC3675_REG_BUCK2 = 0x02,
+ LTC3675_REG_BUCK3 = 0x03,
+ LTC3675_REG_BUCK4 = 0x04,
+ LTC3675_REG_BOOST = 0x05,
+ LTC3675_REG_BUCK_BOOST = 0x06,
+ LTC3675_REG_LED_CONFIG = 0x07,
+ LTC3675_REG_LED_DAC = 0x08,
+ LTC3675_REG_UVOT = 0x09,
+ LTC3675_REG_RSTB = 0xA0,
+ LTC3675_REG_IRQB_MASK = 0x0B,
+ LTC3675_REG_REALTIME_STATUS = 0x0C,
+ LTC3675_REG_LATCHED_STATUS = 0x0D,
+ LTC3675_REG_CLEAR_IRQ = 0x0F
+};
+
+enum LTC3675StatusBits
+{
+ LTC3675_UnderVoltage = 1 << 7,
+ LTC3675_OverTemperature = 1 << 6,
+ LTC3675_BuckBoost_PGood = 1 << 5,
+ LTC3675_Boost_PGood = 1 << 4,
+ LTC3675_Buck4_PGood = 1 << 3,
+ LTC3675_Buck3_PGood = 1 << 2,
+ LTC3675_Buck2_PGood = 1 << 1,
+ LTC3675_Buck1_PGood = 1 << 0
+};
+
+#define LTC3675_DEFAULT_BUCK_REG_VAL 0x6F
+#define LTC3675_DEFAULT_BOOST_REG_VAL 0x0F
+#define LTC3675_DEFAULT_BUCK_BOOST_REG_VAL 0x0F
+
+#define LTC3675_ENABLE_REGISTER_BIT 0x80
+
+// Max I2C rate = 400kHz
+
+static void _ltc3675_clear_irq()
+{
+ // Two-stage clear
+ i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_CLEAR_IRQ, 0x00, _ltc3675_pull_up);
+ i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_NONE, 0x00, _ltc3675_pull_up);
+}
+
+volatile uint8_t _ltc3675_last_status = 0x00;
+
+uint8_t ltc3675_get_last_status(void)
+{
+ return _ltc3675_last_status;
+}
+
+uint8_t ltc3675_reg_status_to_error(uint8_t val)
+{
+ if (((val & LTC3675_BuckBoost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_6)))
+ return BlinkError_3_3V_Peripherals_Power;
+
+ if (((val & LTC3675_Boost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_5)))
+ return BlinkError_TX_Power;
+
+ //if (((val & LTC3675_Buck4_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_4)))
+
+ if (((val & LTC3675_Buck3_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_3)))
+ return BlinkError_1_8V_Peripherals_Power;
+
+ //if (((val & LTC3675_Buck2_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_2)))
+
+ if (((val & LTC3675_Buck1_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_1)))
+ return BlinkError_DRAM_Power;
+
+ return BlinkError_None;
+}
+
+bool ltc3675_is_power_good(uint8_t val)
+{
+ return (ltc3675_reg_status_to_error(val) == BlinkError_None);
+}
+
+uint8_t ltc3675_status_to_error(uint8_t val)
+{
+ if (val & LTC3675_UnderVoltage)
+ return BlinkError_LTC3675_UnderVoltage;
+
+ if (val & LTC3675_OverTemperature)
+ return BlinkError_LTC3675_OverTemperature;
+
+ uint8_t reg_error = ltc3675_reg_status_to_error(val);
+ if (reg_error != BlinkError_None)
+ return reg_error;
+
+ return BlinkError_None;
+}
+
+bool _ltc3675_handle_irq(void)
+{
+ uint8_t val = 0x00;
+ bool result = false;
+
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_LATCHED_STATUS, &val, _ltc3675_pull_up))
+ {
+ debug_log_ex("3675LTCH ", false);
+ debug_log_hex(val);
+ }
+
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, /*LTC3675_REG_LATCHED_STATUS*/LTC3675_REG_REALTIME_STATUS, &val, _ltc3675_pull_up)) // No point acting on latched because could have been resolved
+ {
+ //debug_log_ex("3675LTCH ", false);
+ debug_log_ex("3675RT ", false);
+ debug_log_hex(val);
+
+ _ltc3675_last_status = val;
+
+ uint8_t error = ltc3675_status_to_error(val);
+
+ /*if (val & LTC3675_UnderVoltage)
+ {
+ pmc_set_blink_error(BlinkError_LTC3675_UnderVoltage);
+ //_state.low_battery = true;
+ }*/
+
+ if (error)
+ {
+ pmc_set_blink_error(error);
+
+ /*_i2c_disable_ack_check = true;
+ uint8_t chk = 0x00;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_6) << 0;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_5) << 1;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_3) << 2;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_1) << 3;
+ i2c_write_ex(PWR_SDA, PWR_SCL, 0xFE, 0xFF, chk, _ltc3675_pull_up);
+ _i2c_disable_ack_check = false;*/
+ }
+
+ result = true;
+ }
+
+ _ltc3675_clear_irq();
+
+ return result;
+}
+
+static bool _ltc3675_get_realtime_status(uint8_t* val)
+{
+ //cli();
+
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_REALTIME_STATUS, val, _ltc3675_pull_up) == false)
+ return false;
+
+ debug_log_ex("3675RT ", false);
+ debug_log_hex(*val);
+
+ //sei();
+
+ return true;
+}
+
+int8_t ltc3675_check_status(void)
+{
+ uint8_t val = 0x00;
+
+ pmc_mask_irqs(true);
+
+ bool result = _ltc3675_get_realtime_status(&val);
+
+ pmc_mask_irqs(false);
+
+ if (result == false)
+ return -1;
+
+ //_ltc3675_last_status = val;
+
+ /*if (val & LTC3675_UnderVoltage)
+ return BlinkError_LTC3675_UnderVoltage;
+
+ if (val & LTC3675_OverTemperature)
+ return BlinkError_LTC3675_OverTemperature;
+
+ return BlinkError_None;*/
+
+ return ltc3675_status_to_error(val);
+}
+
+bool ltc3675_handle_irq(void)
+{
+ pmc_mask_irqs(true);
+
+ /*uint8_t*/bool result = _ltc3675_handle_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+static bool _ltc3675_default_reg_helper(uint8_t address)
+{
+ uint8_t val = 0x00;
+ i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, address, &val, _ltc3675_pull_up);
+ return ((val & LTC3675_ENABLE_REGISTER_BIT) == LTC3675_ENABLE_REGISTER_BIT);
+}
+
+bool ltc3675_init(ltc3675_reg_helper_fn helper)
+{
+ if (helper)
+ _ltc3675_reg_helper = helper;
+ else
+ _ltc3675_reg_helper = _ltc3675_default_reg_helper;
+#ifdef HARDWIRE_ENABLE
+ io_output_pin(PWR_EN1);
+ io_output_pin(PWR_EN2);
+ io_output_pin(PWR_EN3);
+ io_output_pin(PWR_EN4);
+ io_output_pin(PWR_EN5);
+#endif // HARDWIRE_ENABLE
+
+ /* io_output_pin(PWR_SDA);
+ io_output_pin(PWR_SCL);
+
+ // Must remain HIGH when idle
+ io_set_pin(PWR_SDA);
+ io_set_pin(PWR_SCL);
+*/
+#ifdef I2C_REWORK
+ i2c_init_ex(PWR_SDA, PWR_SCL, _ltc3675_pull_up);
+#endif // I2C_REWORK
+ io_input_pin(PWR_IRQ);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP)
+ io_set_pin(PWR_IRQ); // Enable pull-up for Open Drain
+#endif // DEBUG
+
+ io_input_pin(WAKEUP);
+ io_set_pin(WAKEUP); // Enable pull-up for Open Drain
+
+ io_input_pin(ONSWITCH_DB);
+ io_set_pin(ONSWITCH_DB); // Enable pull-up for Open Drain
+
+ io_input_pin(PWR_RESET);
+ io_set_pin(PWR_RESET); // Enable pull-up for Open Drain
+
+ _ltc3675_clear_irq(); // Clear old interrupt - state might have changed (e.g. undervoltage might have been resolved)
+
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_IRQB_MASK, 0xFF, _ltc3675_pull_up) == false) // Any PGOOD fault will pull IRQB low
+ return false;
+
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_UVOT, 0x70, _ltc3675_pull_up) == false) // 3.4V UV
+ return false;
+
+ if (ltc3675_has_interrupt())
+ _ltc3675_handle_irq();
+
+ // Non-maskable:
+ // UV warning threshold (default): 2.7V
+ // Over temp warning threshold (default): 10 degrees below
+
+ return true;
+}
+
+bool ltc3675_is_waking_up(void)
+{
+ return io_test_pin(WAKEUP);
+}
+
+static bool _ltc3675_is_pgood(uint8_t reg)
+{
+ uint8_t val = 0x00;
+ if (_ltc3675_get_realtime_status(&val) == false)
+ return false;
+ return ((reg & val) == reg);
+}
+
+static bool _ltc3675_toggle_reg(uint8_t addr, uint8_t def_reg, bool on)
+{
+ bool result = true;
+
+ //cli();
+
+ uint8_t val = 0x00 | def_reg;
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, addr, &val, _ltc3675_pull_up) == false)
+ return false;
+
+ val &= ~LTC3675_ENABLE_REGISTER_BIT;
+
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, addr, /*def_reg*/val | (on ? LTC3675_ENABLE_REGISTER_BIT : 0x00), _ltc3675_pull_up) == false)
+ //return true;
+ result = false;
+
+ if (on)
+ {
+ _delay_ms(LTC3675_REGULATOR_ENABLE_DELAY);
+ }
+
+ //sei();
+
+ return result;
+ //return true;
+}
+
+bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on)
+{
+ //debug_blink2(reg + 1);
+ debug_log_ex("3675 ", false);
+ debug_log_byte_ex(reg, true);
+
+ // Sub-address: index of regulator
+ // Data: <default reg contents> | <enable>
+
+ bool result = false;
+
+ switch (reg)
+ {
+ case LTC3675_REG_1: // Master
+ case LTC3675_REG_2: // Slave
+#ifdef HARDWIRE_ENABLE
+ io_enable_pin(PWR_EN1, on);
+ //break;
+#else
+ //debug_blink2(reg + 1);
+ if (_ltc3675_toggle_reg(LTC3675_REG_BUCK1, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false) {
+ //debug_blink2(reg + 1);
+ return false;
+ }
+ //debug_blink2(reg + 1);
+#endif // HARDWIRE_ENABLE
+ result = (_ltc3675_is_pgood(LTC3675_Buck1_PGood) == on);
+ break;
+ case LTC3675_REG_3: // Master
+ case LTC3675_REG_4: // Slave
+#ifdef HARDWIRE_ENABLE
+ io_enable_pin(PWR_EN3, on);
+ //break;
+#else
+ if (_ltc3675_toggle_reg(LTC3675_REG_BUCK3, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false)
+ return false;
+#endif // HARDWIRE_ENABLE
+ result = (_ltc3675_is_pgood(LTC3675_Buck3_PGood) == on);
+ break;
+ case LTC3675_REG_5: // I2C only
+ if (_ltc3675_toggle_reg(LTC3675_REG_BOOST, LTC3675_DEFAULT_BOOST_REG_VAL, on) == false) // (Boost address, Default reg contents | Enable)
+ return false;
+ result = (_ltc3675_is_pgood(LTC3675_Boost_PGood) == on);
+ break;
+ case LTC3675_REG_6: // Single
+#ifdef HARDWIRE_ENABLE
+ io_enable_pin(PWR_EN5, on);
+ //break;
+#else
+ if (_ltc3675_toggle_reg(LTC3675_REG_BUCK_BOOST, LTC3675_DEFAULT_BUCK_BOOST_REG_VAL, on) == false)
+ return false;
+#endif // HARDWIRE_ENABLE
+ result = (_ltc3675_is_pgood(LTC3675_BuckBoost_PGood) == on);
+ break;
+ //default:
+ // return false;
+ }
+
+ _debug_log((result ? "+" : "-"));
+
+ return result;
+}
+
+bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage)
+{
+ // Not necessary due to R-bridges and default DAC registers
+
+ // VRAM will be 1.3579 - a little high? (re-program DAC reference)
+ // No: minimum FB step will put Vout < 1.35
+
+ uint16_t max_voltage = 0;
+ uint8_t reg_subaddr = 0;
+
+ switch (reg)
+ {
+ case LTC3675_REG_1: // 1A Buck
+ case LTC3675_REG_2: // 1A Buck
+ max_voltage = 1500;
+ reg_subaddr = LTC3675_REG_BUCK1;
+ break;
+ case LTC3675_REG_3: // 500mA Buck
+ case LTC3675_REG_4: // 500mA Buck
+ max_voltage = 1800;
+ reg_subaddr = LTC3675_REG_BUCK3;
+ break;
+ case LTC3675_REG_5: // 1A Boost
+ max_voltage = 5000;
+ reg_subaddr = LTC3675_REG_BOOST;
+ break;
+ case LTC3675_REG_6: // 1A Buck-Boost
+ max_voltage = 3300;
+ reg_subaddr = LTC3675_REG_BUCK_BOOST;
+ break;
+ }
+
+ if (voltage > max_voltage)
+ return false;
+
+ //uint32_t rMax = ((uint32_t)voltage * 1000) / (uint32_t)max_voltage;
+ //uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800;
+ uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800; // 800mV full-scale feedback voltage
+ uint32_t r = ((uint32_t)voltage * 1000) / (uint32_t)rFB;
+ if (r < 450)
+ return false;
+
+ uint16_t rDAC = (16 * ((uint16_t)r - 450)) / (800 - 450);
+
+ debug_log_ex("Vr ", false);
+ debug_log_byte_ex(reg, false);
+ debug_log_ex("=", false);
+ debug_log_byte_ex((uint8_t)rDAC, false);
+
+ uint8_t val = 0x00;
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, reg_subaddr, &val, _ltc3675_pull_up) == false)
+ {
+ debug_log("-");
+ return false;
+ }
+
+ val = (val & 0xF0) | (uint8_t)rDAC;
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, reg_subaddr, val, _ltc3675_pull_up) == false)
+ {
+ debug_log("-");
+ return false;
+ }
+
+ debug_log("+");
+
+ return true;
+}
+
+bool ltc3675_is_power_button_depressed(void)
+{
+ return (io_test_pin(ONSWITCH_DB) == false);
+}
+
+bool ltc3675_has_interrupt(void)
+{
+ return (io_test_pin(PWR_IRQ) == false);
+}
diff --git a/firmware/e300/rev_b/ltc3675.h b/firmware/e300/rev_b/ltc3675.h
new file mode 100644
index 000000000..d7c4baa59
--- /dev/null
+++ b/firmware/e300/rev_b/ltc3675.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Ettus Research LLC
+ */
+
+#ifndef LTC3675_H
+#define LTC3675_H
+
+//#include "types.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef bool (*ltc3675_reg_helper_fn)(uint8_t address);
+
+bool ltc3675_init(ltc3675_reg_helper_fn helper);
+
+typedef enum ltc3675_regulators {
+ LTC3675_REG_1, // 1A Buck
+ LTC3675_REG_2, // 1A Buck
+ LTC3675_REG_3, // 500mA Buck
+ LTC3675_REG_4, // 500mA Buck
+ LTC3675_REG_5, // 1A Boost
+ LTC3675_REG_6 // 1A Buck-Boost
+ // LED Boost
+} ltc3675_regulator_t;
+
+bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on);
+bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage);
+bool ltc3675_is_power_button_depressed(void);
+bool ltc3675_has_interrupt(void);
+bool ltc3675_handle_irq(void);
+int8_t ltc3675_check_status(void);
+uint8_t ltc3675_get_last_status(void);
+uint8_t ltc3675_status_to_error(uint8_t val);
+bool ltc3675_is_power_good(uint8_t val);
+bool ltc3675_is_waking_up(void);
+
+#endif /* LTC3675_H */
diff --git a/firmware/e300/rev_b/ltc4155.c b/firmware/e300/rev_b/ltc4155.c
new file mode 100644
index 000000000..5f404e651
--- /dev/null
+++ b/firmware/e300/rev_b/ltc4155.c
@@ -0,0 +1,402 @@
+/*
+ * ltc4155.c
+ */
+
+#ifndef CHARGER_TI
+
+#include "config.h"
+#include "ltc4155.h"
+
+#include <util/delay.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "power.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+static io_pin_t USBPM_IRQ = IO_PB(1);
+
+#ifdef ATTINY88_DIP
+
+static io_pin_t CHRG_SDA = IO_PC(2);
+static io_pin_t CHRG_SCL = IO_PC(3);
+
+#else
+
+#ifdef I2C_REWORK
+
+static io_pin_t CHRG_SDA = IO_PC(4);
+static io_pin_t CHRG_SCL = IO_PC(5);
+
+#else
+
+#define CHRG_SDA PWR_SDA
+#define CHRG_SCL PWR_SCL
+
+#endif // I2C_REWORK
+
+#endif // ATTINY88_DIP
+
+const bool _ltc4155_pull_up = false;
+
+#define LTC4155_BASE_ADDRESS 0x12
+#define LTC4155_WRITE_ADDRESS (LTC4155_BASE_ADDRESS + 0)
+#define LTC4155_READ_ADDRESS (LTC4155_BASE_ADDRESS + 1)
+/*
+#define LTC4155_RETRY_DELAY 1 // us MAGIC
+#define LTC4155_MAX_ACK_RETRIES 10 // * LTC4155_RETRY_DELAY us
+
+#define LTC4155_SCL_LOW_PERIOD 2 // 1.3 us
+#define LTC4155_SCL_HIGH_PERIOD 1 // 0.6 us
+#define LTC4155_BUS_FREE_TIME 2 // 1.3 us
+#define LTC4155_STOP_TIME 1 // 0.6 us
+*/
+enum LTC4155Registers
+{
+ LTC4155_REG_USB = 0x00, // W/R
+ LTC4155_REG_WALL = 0x01, // W/R
+ LTC4155_REG_CHARGE = 0x02, // W/R
+ LTC4155_REG_STATUS = 0x03, // R
+ LTC4155_REG_GOOD = 0x04, // R
+ LTC4155_REG_THERMISTOR = 0x05, // R
+ LTC4155_REG_ENABLE = 0x06, // W/R
+ LTC4155_REG_ARM_AND_SHIP= 0x07 // W
+};
+
+enum LTC4155InterruptMasks // LTC4155_REG_ENABLE
+{
+ LTC4155_ENABLE_USB_OTG = 1 << 1,
+
+ LTC4155_INT_UVCL = 1 << 2,
+ LTC4155_INT_ILIMIT = 1 << 3,
+ LTC4155_INT_USB_OTG = 1 << 4,
+ LTC4155_INT_EXT_PWR = 1 << 5,
+ LTC4155_INT_FAULT = 1 << 6,
+ LTC4155_INT_CHARGER = 1 << 7
+};
+
+enum LTC4155Options // LTC4155_REG_USB
+{
+ LTC4155_USB_OTG_LOCKOUT = 1 << 5,
+ LTC4155_ENABLE_BATTERY_CONDITIONER = 1 << 6,
+ LTC4155_DISABLE_INPUT_UVCL = 1 << 7
+};
+
+enum LTC4155Shifts
+{
+ LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT = 4,
+ LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE = 2,
+ LTC4155_SHIFTS_WALL_PRIORITY = 7,
+ LTC4155_SHIFTS_WALL_SAFETY_TIMER = 5
+};
+
+enum LTC4155Statuses // LTC4155_REG_STATUS
+{
+ LTC4155_LOW_BATTERY = 1 << 0,
+ LTC4155_BOOST_ENABLE = 1 << 3,
+ LTC4155_ID_PIN_DETECT = 1 << 4,
+};
+
+enum LTC4155Goods // LTC4155_REG_GOOD
+{
+ LTC4155_BAD_CELL_FAULT = 1 << 0,
+ LTC4155_OTG_FAULT = 1 << 1,
+ LTC4155_OVP_ACTIVE = 1 << 2,
+ LTC4155_INPUT_UVCL_ACTIVE = 1 << 3,
+ LTC4155_INPUT_CURRENT_LIMIT_ACTIVE = 1 << 4,
+ LTC4155_WALLSNS_GOOD = 1 << 5,
+ LTC4155_USBSNS_GOOD = 1 << 6,
+ LTC4155_EXTERNAL_POWER_GOOD = 1 << 7
+};
+
+enum LTC4155BatteryChargerStatues
+{
+ LTC4155_CHARGER_OFF,
+ LTC4155_CHARGER_LOW_BATTERY_VOLTAGE,
+ LTC4155_CHARGER_CONSTANT_CURRENT,
+ LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX,
+ LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX,
+ LTC4155_CHARGER_NTC_TOO_WARM,
+ LTC4155_CHARGER_NTC_TOO_COLD,
+ LTC4155_CHARGER_NTC_HOT
+};
+
+enum LTC4155ThermistorStatuses
+{
+ LTC4155_NTC_NORMAL,
+ LTC4155_NTC_TOO_COLD,
+ LTC4155_NTC_TOO_WARM,
+ LTC4155_NTC_FAULT
+};
+
+static const uint8_t _ltc4155_interrupt_mask =
+// LTC4155_ENABLE_USB_OTG |// Enable +5V on USB connector // Is this causing the chip to power off the output?!
+ LTC4155_INT_UVCL |
+ LTC4155_INT_ILIMIT |
+ LTC4155_INT_USB_OTG |
+ LTC4155_INT_EXT_PWR | // Turn up current limit
+ LTC4155_INT_FAULT | // Blink error
+ LTC4155_INT_CHARGER; // Illuminate charge LED
+
+static bool _ltc4155_clear_irq(void)
+{
+ return i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, _ltc4155_interrupt_mask, _ltc4155_pull_up);
+}
+
+bool ltc4155_clear_irq(void)
+{
+ pmc_mask_irqs(true);
+
+ bool result = _ltc4155_clear_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+static uint8_t _ltc4155_last_good, _ltc4155_last_status;
+
+bool _ltc4155_handle_irq(void)
+{
+ _ltc4155_clear_irq(); // Clear frozen registers to get the real-time ones
+
+ _delay_ms(50); // Wait for registers to clear/update
+
+ //////////////////
+
+ uint8_t val = 0x00;
+ bool result = false;
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ //if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
+ // goto _ltc4155_handle_fail;
+
+ debug_log_ex("4155GO ", false);
+ debug_log_hex(val);
+
+ if (val & LTC4155_WALLSNS_GOOD)
+ {
+ uint8_t wall_state = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ wall_state &= ~0x1E;
+ wall_state |= 0x0E;
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ debug_log("I+");
+ }
+
+ _ltc4155_last_good = val;
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ //if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
+ // goto _ltc4155_handle_fail;
+
+ debug_log_ex("4155ST ", false);
+ debug_log_hex(val);
+
+ _ltc4155_last_status = val;
+
+ val >>= 5;
+
+ if (_state.blink_error == BlinkError_None)
+ {
+ switch (val)
+ {
+ case LTC4155_CHARGER_CONSTANT_CURRENT:
+ case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX:
+ case LTC4155_CHARGER_LOW_BATTERY_VOLTAGE: // If this persists for more than 1/2hr, BAD_CELL_FAULT is enabled and FAULT interrupt is generated
+ {
+ if ((_state.battery_not_present == false) &&
+ (_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD)))
+ {
+ //charge_set_led(true);
+ charge_notify(true);
+ break;
+ }
+ }
+ case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX: // Small amount of current still charging the battery but below Vc/x threshold
+ //case LTC4155_CHARGER_NTC_TOO_WARM:
+ //case LTC4155_CHARGER_NTC_TOO_COLD:
+ //case LTC4155_CHARGER_NTC_HOT:
+ // break;
+ //case LTC4155_CHARGER_OFF:
+ default:
+ //charge_set_led(false);
+ charge_notify(false);
+ }
+ }
+
+// ltc4155_dump();
+
+ result = true;
+_ltc4155_handle_fail:
+ _ltc4155_clear_irq(); // Even though it happens first above, this is necessary otherwise future IRQs won't be detected
+
+ return result;
+}
+
+#define LTC4155_CHARGE_CURRENT_LIMIT /*0xF*/0x7 // [100%] 50%
+
+bool ltc4155_set_charge_current_limit(uint8_t percentage)
+{
+ uint8_t val = 0;
+ uint8_t limit = 0;
+
+ if (percentage > 100)
+ return false;
+ else if (percentage == 100)
+ percentage = 0xF;
+ else if (percentage > 12) // 0..88 -> 0..8800
+ {
+ uint16_t l = (((uint16_t)percentage - 12) * 100) / 586;
+ limit = (uint8_t)l;
+ }
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, &val, _ltc4155_pull_up) == false)
+ return false;
+
+ val &= ((0x1 << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT) - 1);
+ //val |= (LTC4155_CHARGE_CURRENT_LIMIT << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT);
+ val |= (limit << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT);
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, val, _ltc4155_pull_up) == false)
+ return false;
+
+//ltc4155_dump();
+
+ return true;
+}
+
+bool ltc4155_init(bool disable_charger)
+{
+ io_input_pin(USBPM_IRQ);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP)
+ io_set_pin(USBPM_IRQ); // Enable pull-up for Open Drain
+#endif // DEBUG
+#ifdef I2C_REWORK
+ i2c_init_ex(CHRG_SDA, CHRG_SCL, _ltc4155_pull_up);
+#endif // I2C_REWORK
+ if (/*_ltc4155_clear_irq()*/_ltc4155_handle_irq() == false) // Will set interrupt masks // FIXME: Why does this cause instability?!
+ return false;
+
+ const uint8_t charge_state =
+ (disable_charger ? 0x0 : LTC4155_CHARGE_CURRENT_LIMIT) << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT | // Battery charger I limit = 100%
+ 0x3 << LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE | // FIXME: Vbatt float = 4.05V - 4.2V for LiPo (default 0x00)
+ 0x0; // Full capacity charge threshold = 10%
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, charge_state, _ltc4155_pull_up) == false)
+ return false;
+
+ const uint8_t wall_state =
+ 0x0 << LTC4155_SHIFTS_WALL_PRIORITY |
+ 0x0 << LTC4155_SHIFTS_WALL_SAFETY_TIMER | // Charge safety timer = 4hr // FIXME: 8hr or Vc/x
+ 0xE; // 3 amps, 0x1F - CLPROG1
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
+ return false;
+
+ // FIXME:
+ // Disable ID pin detection & autonomous startup
+ // Enable OTG
+ //i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_USB, LTC4155_USB_OTG_LOCKOUT, _ltc4155_pull_up); // Disable autonomous startup
+ //i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, LTC4155_ENABLE_USB_OTG, _ltc4155_pull_up); // Enable OTG
+
+ if (_ltc4155_handle_irq() == false) // One more time (IRQ LED stays lit in dev setup)
+ return false;
+
+ return true;
+}
+
+bool ltc4155_has_interrupt(void)
+{
+ //bool state = io_test_pin(USBPM_IRQ);
+ //debug_log_ex("4155IRQ", false);
+ //debug_log_byte(state);
+ //return (state != 1);
+ return (io_test_pin(USBPM_IRQ) == false);
+}
+
+bool ltc4155_handle_irq(void)
+{
+ pmc_mask_irqs(true);
+
+ bool result = _ltc4155_handle_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+bool ltc4155_arm_ship_and_store(void)
+{
+ return true;
+}
+
+bool ltc4155_get_thermistor(uint8_t* val, bool* warning)
+{
+ bool result = false;
+ uint8_t _val = 0;
+
+ pmc_mask_irqs(true);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_THERMISTOR, &_val, _ltc4155_pull_up) == false)
+ goto ltc4155_get_thermistor_fail;
+
+ if (val)
+ (*val) = _val >> 1;
+
+ if (warning)
+ (*warning) = ((_val & 0x01) != 0x00);
+
+ result = true;
+ltc4155_get_thermistor_fail:
+ pmc_mask_irqs(false);
+ return result;
+}
+
+void ltc4155_dump(void)
+{
+ pmc_mask_irqs(true);
+
+ uint8_t val = 0x00;
+ bool warning = false;
+
+ if (ltc4155_get_thermistor(&val, &warning) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\tTHRM", false);
+ if (warning)
+ debug_log_ex("!", false);
+ debug_log_byte(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &val, _ltc4155_pull_up) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\tWALL", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\t4155GO ", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\t4155ST ", false);
+ debug_log_hex(val);
+
+ltc4155_dump_fail:
+ pmc_mask_irqs(false);
+}
+
+#endif // !CHARGER_TI
diff --git a/firmware/e300/rev_b/ltc4155.h b/firmware/e300/rev_b/ltc4155.h
new file mode 100644
index 000000000..7e8e3751d
--- /dev/null
+++ b/firmware/e300/rev_b/ltc4155.h
@@ -0,0 +1,25 @@
+/*
+ * ltc4155.h
+ *
+ * Created: 17/08/2012 8:09:43 PM
+ * Author: Balint Seeber
+ */
+
+
+#ifndef LTC4155_H_
+#define LTC4155_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifndef CHARGER_TI
+
+bool ltc4155_init(bool disable_charger);
+bool ltc4155_has_interrupt(void);
+bool ltc4155_handle_irq(void);
+void ltc4155_dump(void);
+bool ltc4155_set_charge_current_limit(uint8_t percentage);
+
+#endif // !CHARGER_TI
+
+#endif /* LTC4155_H_ */
diff --git a/firmware/e300/rev_b/main.c b/firmware/e300/rev_b/main.c
new file mode 100644
index 000000000..1e065ffef
--- /dev/null
+++ b/firmware/e300/rev_b/main.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2009 Ettus Research LLC
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+
+#include "global.h"
+#include "power.h"
+#include "debug.h"
+#include "error.h"
+#include "ltc3675.h"
+#ifdef CHARGER_TI
+#include "bq24190.h"
+#else
+#include "ltc4155.h"
+#endif // CHARGER_TI
+
+#define AUTO_POWER_ON
+
+#define INITIAL_DELAY 250 // ms
+
+FUSES = { // FIXME: & FUSE_CKSEL1 for low power 128 kHz clock
+ .low = (FUSE_CKSEL0 & FUSE_SUT0 & FUSE_CKDIV8), // Internal 8MHz Oscillator, Slowly rising power (start-up time), Divide Clock by 8
+ .high = (FUSE_EESAVE & FUSE_SPIEN), // Save EEPROM between flashes // FIXME: Leave SPIEN for programming enabled?
+}; // Not using watchdog as it runs during sleep and consumes power
+
+volatile STATE _state;
+
+/*
+ - Main/shared variables must be volatile
+ - Port pins are tri-stated on reset
+ * AVR_IRQ PD(5)
+ - Enable pull-ups on all O.D. outputs from regulator chip
+ * PS_POR/SRST should be driven HIGH by ATTiny?
+ - AVR_RESET -> RESET pin - don't configure fuse (this would disable this functionality and prohibit serial programming)
+ * Ship-and-store mode for charge controller?
+ * cli before I2C calls
+ * PS_TX
+ - en5-clk, en2-data
+ * Instruction following SEI is executed before interrupts
+ * LTC3675 real-time status doesn't contain UV/OT
+ * LTC3675 PGOOD -> power down (no point in checking blink state)
+ * On WALL, use TX, on battery use OTG switcher
+ * PRR - Power Reduction Register (p40)
+ - 100% -> 50% battery charge limit
+ * Check latched status for UV/OT in 3675
+ * If blink error reset, get latest charge status from 4155
+ * Fix UV status check from 3675/4155 as they currently share the same error
+ * Use charger termination at 8hr or Vc/x
+ * Check PGood on all regs after power on before setting powered=true
+ * Re-init 4155 on soft-power on
+ - Re-set 3A limit in 4155 after external power connection
+ - Removing power when running on battery, 4155GO 0xA0 - but WALL has been removed
+ - Why is charger reporting Constant Current when power is removed
+ * ltc3675_is_power_button_depressed: check if any reg is on, otherwise value will be invalid
+ * When e.g. 3.3V doesn't come up, blink code is correctly 4 but there's a very short blink before re-starting the sequence
+ - Vprog<Vc/x
+*/
+
+bool pmc_mask_irqs(bool mask)
+{
+ if (_state.interrupts_enabled == false)
+ return false;
+
+ if (mask)
+ {
+ if (_state.interrupt_depth == 0)
+ cli();
+ ++_state.interrupt_depth;
+ }
+ else
+ {
+ if (_state.interrupt_depth == 0)
+ return false;
+
+ --_state.interrupt_depth;
+ if (_state.interrupt_depth == 0)
+ sei();
+ }
+
+ return true;
+}
+
+int main(void)
+{
+ _delay_ms(INITIAL_DELAY);
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ memset((void*)&_state, 0x00, sizeof(STATE));
+
+ debug_init();
+ debug_blink(1);
+
+ //debug_log("#"); // Will not boot if this is 21 chars long?!
+ debug_log("Hello world");
+
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN); // SLEEP_MODE_PWR_SAVE combination is documented as Reserved
+
+//ltc4155_dump();
+
+ // FIXME: Init as SPI slave (FPGA is master)
+
+ // 8-bit timer for blinking errors on charge LED
+ TCCR0A = _BV(CTC0); // CTC mode
+ OCR0A = 244; // 250ms with 1024 prescale
+ TIMSK0 = _BV(OCIE0A); // Enable CTC on Timer 0
+
+ bool init_result = power_init();
+ debug_log_ex("Init", false);
+ _debug_log(init_result ? "+" : "-");
+ debug_blink(2);
+ //debug_blink_rev(6);
+
+ ///////////////////////////////////
+#ifdef AUTO_POWER_ON
+ power_on(); // Turn on immediately. Need to de-press power button to turn off.
+ debug_log("Power");
+ debug_blink(3);
+ //debug_blink_rev(10);
+
+ //debug_wait();
+
+//ltc4155_dump();
+#endif // AUTO_POWER_ON
+ _state.interrupts_enabled = true;
+ sei(); // Enable interrupts
+
+ asm("nop");
+
+ _state.wake_up = false; // This will fire the first time the regs are turned on
+
+ bool one_more = false;
+
+ while (true)
+ {
+ one_more = false;
+#ifdef CHARGER_TI
+ if (_state.bq24190_irq)
+ {
+ bq24190_handle_irq();
+
+ _state.bq24190_irq = false;
+ }
+#else
+ if ((_state.ltc4155_irq)/* || ltc4155_has_interrupt()*/) // [Don't know why PCINT ISR misses LTC4155 IRQ on power up, so double-check state of line]
+ {
+ ltc4155_handle_irq();
+//ltc4155_dump();
+ _state.ltc4155_irq = false;
+ }
+#endif // !CHARGER_TI
+ if (_state.core_power_bad) // FIXME: Check whether it's supposed to be on
+ {
+ if (power_is_subsys_on(PS_FPGA))
+ {
+ _delay_ms(1); // Seeing weird 120us drop in PGOOD during boot from flash (no apparent drop in 1.0V though)
+
+ if (tps54478_is_power_good() == false)
+ {
+ debug_log("ML:FPGA!");
+
+ //power_off();
+ _state.power_off = true;
+
+ /*while (_state.wake_up == false)
+ {
+ blink_error_sequence(1);
+ }*/
+ pmc_set_blink_error(BlinkError_FPGA_Power); // [If a blink error was set in power_off, this will supercede it]
+ }
+ }
+
+ _state.core_power_bad = false;
+ }
+
+ if ((_state.ltc3675_irq)/* || ltc3675_has_interrupt()*/) // This is fired on initial power up
+ {
+ debug_log("ML:3675+");
+
+ ltc3675_handle_irq();
+
+ if (ltc3675_is_power_good(ltc3675_get_last_status()) == false)
+ {
+ debug_log("ML:3675!");
+
+ //power_off();
+ _state.power_off = true;
+ }
+
+ _state.ltc3675_irq = false;
+ }
+
+ if (_state.power_off)
+ {
+ debug_log("ML:Off..");
+
+ power_off();
+
+ _state.power_off = false;
+ _state.wake_up = false;
+ }
+ else if (_state.wake_up)
+ {
+ _delay_ms(1); // Tapping 3.1 ohm load ing 4155 in dev setup causes transient on this line and causes power on sequence to begin again
+
+ //if (_state.powered == false) // Don't check in case button is held long enough to force LTC3675 shutdown (will not change 'powered' value)
+ if (ltc3675_is_waking_up())
+ {
+ debug_log("ML:On..");
+
+ power_on();
+ }
+
+ _state.wake_up = false;
+ }
+
+ // Check to see if the error state has resolved itself at the end of each sequence of the current blink error
+
+ if ((_state.blink_error != BlinkError_None) && (_state.blink_last_loop != _state.blink_loops))
+ {
+ // [Check IRQs periodically]
+
+ bool ltc3675_use_last_status = false;
+ /*if (ltc3675_has_interrupt())
+ {
+ //debug_set(IO_PB(6), ((_state.blink_loops % 2) == 0));
+ ltc3675_use_last_status = true;
+ ltc3675_handle_irq();
+ }*/
+
+ ///////////////////////////
+
+ switch (_state.blink_error)
+ {
+ case BlinkError_LTC3675_UnderVoltage:
+ case BlinkError_LTC3675_OverTemperature:
+ case BlinkError_DRAM_Power:
+ case BlinkError_3_3V_Peripherals_Power:
+ case BlinkError_1_8V_Peripherals_Power:
+ case BlinkError_TX_Power:
+ if (((ltc3675_use_last_status) && (ltc3675_status_to_error(ltc3675_get_last_status()) != BlinkError_None)) ||
+ ((ltc3675_use_last_status == false) && (ltc3675_check_status() != BlinkError_None)))
+ break;
+ debug_log("BE:3675-");
+ goto cancel_blink_error;
+ case BlinkError_FPGA_Power:
+ if (tps54478_is_power_good() == false)
+ break;
+ debug_log("BE:FPGA-");
+ goto cancel_blink_error;
+ default:
+cancel_blink_error:
+ //debug_set(IO_PB(7), true);
+ pmc_set_blink_error(BlinkError_None);
+ }
+
+ ////////////////////////////////////
+
+ // More periodic checks
+ // Need to do this has some interrupts are on PCINT, and while GIE is disabled, might change & change back
+ // E.g. LTC3675 IRQ due to UV, reset IRQ, re-asserts UV
+#ifndef CHARGER_TI
+ if (ltc4155_has_interrupt())
+ {
+ debug_log("BE:4155");
+
+ _state.ltc4155_irq = true;
+ one_more = true;
+ }
+#endif // !CHARGER_TI
+ if (ltc3675_has_interrupt())
+ {
+ debug_log("BE:3675");
+
+ _state.ltc3675_irq = true;
+ one_more = true;
+ }
+
+ if (power_is_subsys_on(PS_FPGA))
+ {
+ if (tps54478_is_power_good() == false)
+ {
+ debug_log("BE:FPGA!");
+
+ _state.core_power_bad = true;
+ one_more = true;
+ }
+ }
+
+ ////////////////////////////////////
+
+ _state.blink_last_loop = _state.blink_loops;
+ }
+
+ //if (_state.timers_running == false)
+ if ((_state.active_timers == 0) && (one_more == false))
+ {
+ debug_log("^");
+ sleep_mode();
+ debug_log("$");
+ }
+ }
+
+ return 0;
+}
+
+uint8_t pmc_get_blink_error(void)
+{
+ return _state.blink_error;
+}
+
+void pmc_set_blink_error(uint8_t count)
+{
+ if ((_state.blink_error != BlinkError_None) && (count /*> _state.blink_error*/!= BlinkError_None)) // [Prioritise] Always keep first sequence running
+ return;
+ else if (_state.blink_error == count) // Don't restart if the same
+ return;
+
+ if (count == BlinkError_None)
+ {
+ debug_log("BLNK-");
+ _state.blink_stop = true;
+ return;
+ }
+
+ //char msg[25];
+ //sprintf(msg, "Blink code = %i\n", count);
+ //debug_log(msg);
+ debug_log_ex("BLNK ", false);
+ debug_log_byte(count);
+
+ _state.blink_error = count;
+ _state.blink_loops = 0;
+ _state.blink_last_loop = 0;
+ _state.blinker_state = 0;
+ _state.blink_stop = false;
+
+ charge_set_led(false);
+
+ TCNT0 = 0;
+ if ((TCCR0A & 0x07) == 0x00) // Might already be active with existing error
+ _state.active_timers++;
+ TCCR0A |= 0x05; // Start with 1024 prescale
+}
+
+ISR(TIMER0_COMPA_vect) // Blink the sequence, and leave one slot at the beginning and end where the LED is off so one can get a sense of how many blinks occurred
+{
+ pmc_mask_irqs(true);
+
+ if (_state.blinker_state < (2 * _state.blink_error + 1))
+ charge_set_led((_state.blinker_state % 2) == 1);
+
+ _state.blinker_state++;
+
+ if (_state.blinker_state == (2 * _state.blink_error + 1 + 1))
+ {
+ _state.blinker_state = 0;
+
+ if (_state.blink_stop)
+ {
+ if ((TCCR0A & 0x07) != 0x00)
+ _state.active_timers--;
+ TCCR0A &= ~0x07;
+
+ _state.blink_error = BlinkError_None;
+
+ debug_log("BLNK.");
+ }
+ else
+ {
+ _state.blink_loops++;
+ }
+ }
+
+ pmc_mask_irqs(false);
+}
diff --git a/firmware/e300/rev_b/power.c b/firmware/e300/rev_b/power.c
new file mode 100644
index 000000000..79864f817
--- /dev/null
+++ b/firmware/e300/rev_b/power.c
@@ -0,0 +1,909 @@
+/*
+ * Test battery voltage code
+ * Charger error blinking uses busy wait - will drain battery if encounters error while unattended? Surely rest of H/W will pull more current.
+*/
+#include "config.h"
+#include "power.h"
+
+#include <string.h>
+#include <util/delay.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "ltc3675.h"
+#include "ltc4155.h"
+#include "bq24190.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+#define BLINK_ERROR_DELAY 250 // ms
+
+#define POWER_DEFAULT_DELAY 50 // ms
+#define POWER_DEFAULT_RETRIES 10
+
+#define BATT_MIN_VOLTAGE 2000 // mV
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define ZERO_MEMORY(s) memset(&s, 0x00, sizeof(s))
+
+#ifndef I2C_REWORK
+io_pin_t PWR_SDA = IO_PC(4);
+io_pin_t PWR_SCL = IO_PC(5);
+
+io_pin_t USBHUB_RESET= IO_PA(2);
+#endif // I2C_REWORK
+
+//volatile bool powered = false;
+
+#ifdef DDR3L
+#define DRAM_VOLTAGE 1350
+#else
+#define DRAM_VOLTAGE 0 // Hardware default
+#endif // DDR3
+
+struct reg_config {
+ int16_t voltage; // mV
+ uint8_t device;
+ uint8_t address; // Device specific
+ bool powered;
+} default_reg_config[] = { // Index maps to 'power_subsystem_t', 0 volts means leave at hardware default
+ { 0000, REG_UNKNOWN, 0/*, true*/ }, // PS_UNKNOWN
+ { 1000, REG_TPS54478, 0/*, true*/ }, // PS_FPGA
+ { DRAM_VOLTAGE, REG_LTC3675, LTC3675_REG_1 }, // PS_VDRAM
+ { /*1800*/0, REG_LTC3675, LTC3675_REG_3 }, // PS_PERIPHERALS_1_8
+ { /*3300*/0, REG_LTC3675, LTC3675_REG_6 }, // PS_PERIPHERALS_3_3
+ { /*5000*/0, REG_LTC3675, LTC3675_REG_5 } // PS_TX
+};
+/*
+int8_t power_get_regulator_index(uint8_t device, uint8_t address)
+{
+ for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i)
+ {
+ struct reg_config* reg = default_reg_config + i;
+ if ((reg->device == device) && (reg->address == address))
+ return i;
+ }
+
+ return -1;
+}
+*/
+bool power_is_subsys_on(power_subsystem_t index)
+{
+ if ((index <= PS_UNKNOWN) || (index >= PS_MAX))
+ return false;
+
+ return default_reg_config[index].powered;
+}
+
+static bool ltc3675_reg_helper(uint8_t address)
+{
+ for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i)
+ {
+ struct reg_config* reg = default_reg_config + i;
+ if ((reg->device == REG_LTC3675) && (reg->address == address))
+ return reg->powered;
+ }
+#ifdef DEBUG_SAFETY
+ debug_log_ex("!3675HLP ", false);
+ debug_log_hex(address);
+#endif // DEBUG_SAFETY
+ return false;
+ //return power_is_subsys_on(power_get_regulator_index(REG_LTC3675, address) - 1);
+}
+
+static io_pin_t AVR_CS = IO_PB(2);
+static io_pin_t AVR_MOSI = IO_PB(3);
+static io_pin_t AVR_MISO = IO_PB(4);
+static io_pin_t AVR_SCK = IO_PB(5);
+
+#ifndef ATTINY88_DIP
+static io_pin_t FTDI_BCD = IO_PB(6);
+static io_pin_t FTDI_PWREN2 = IO_PB(7);
+#endif // ATTINY88_DIP
+
+static io_pin_t AVR_RESET = IO_PC(6);
+static io_pin_t AVR_IRQ = IO_PD(5);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define TPS54478_START_DELAY 10 // 50 (safety) // 3 (per spec) // ms (some arbitrary value so that the external power supply can settle)
+
+#ifdef ATTINY88_DIP
+static io_pin_t CORE_PWR_EN = IO_PC(1); // IO_PC(7) not routed by card, using PWER_EN1 instead
+#else
+static io_pin_t CORE_PWR_EN = IO_PA(3);
+#endif // ATTINY88_DIP
+static io_pin_t CORE_PGOOD = IO_PB(0);
+
+void tps54478_init(bool enable)
+{
+ tps54478_set_power(enable);
+ io_clear_pin(CORE_PWR_EN);
+
+ io_input_pin(CORE_PGOOD);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP) // Don't enable pull-up when connected to a pulled-up switch
+ io_set_pin(CORE_PGOOD); // Enable pull-up for Open Drain
+#endif // DEBUG
+//#ifdef DEBUG
+// io_enable_pin(CORE_PWR_EN, false);
+//#endif // DEBUG
+//_delay_ms(2500);
+}
+
+void tps54478_set_power(bool on)
+{
+ debug_log_ex("54478", false);
+
+ // Assumes: Hi-Z input/LOW output
+
+ if (on)
+ {
+ io_input_pin(CORE_PWR_EN);
+ _delay_ms(TPS54478_START_DELAY);
+
+ debug_log("+");
+ }
+ else
+ {
+ io_output_pin(CORE_PWR_EN);
+ // Don't delay here as we can't detect its state anyway
+
+ debug_log("-");
+ }
+
+ //io_enable_pin(CORE_PWR_EN, on);
+}
+
+bool tps54478_is_power_good(void)
+{
+ return io_test_pin(CORE_PGOOD); // This doesn't necessarily mean it's good - the chip might be malfunctioning (or switched off)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static io_pin_t CHARGE = IO_PD(1);
+
+#if !defined(ATTINY88_DIP) && defined(LED_POLARITY)
+static io_pin_t POWER_LED = IO_PC(7);
+
+void power_set_led_ex(bool on, bool swap)
+{
+ if (swap)
+ {
+ if ((on == false) && (/*io_is_pin_set(CHARGE)*/_state.battery_charging)) // If charging and turning off, don't change charge light
+ {
+ charge_set_led(true); // Force it again just in case
+ return;
+ }
+ }
+
+ io_clear_pin(CHARGE);
+ io_enable_pin(POWER_LED, on);
+}
+
+void power_set_led(bool on)
+{
+ power_set_led_ex(on, true);
+}
+#endif // !ATTINY88_DIP && LED_POLARITY
+
+void charge_set_led_ex(bool on, bool swap)
+{
+#ifdef ATTINY88_DIP
+ //
+#else
+
+#ifdef LED_POLARITY
+ io_clear_pin(POWER_LED);
+#endif // LED_POLARITY
+
+#endif // ATTINY88_DIP
+
+#ifdef ATTINY88_DIP
+ io_enable_pin(CHARGE, !on);
+#else
+ io_enable_pin(CHARGE, on);
+
+#ifdef LED_POLARITY
+ if (swap)
+ {
+ if ((on == false) && (_state.powered)) // If no longer charging, turn power light back on
+ power_set_led(true);
+ }
+#endif // LED_POLARITY
+
+#endif // ATTINY88_DIP
+}
+
+void charge_set_led(bool on)
+{
+ charge_set_led_ex(on, true);
+}
+
+void charge_notify(bool charging)
+{
+ _state.battery_charging = charging;
+
+ charge_set_led(charging);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void usbhub_reset(void)
+{
+#ifndef I2C_REWORK
+ io_clear_pin(USBHUB_RESET);
+
+ _delay_us(1 * 10); // Minimum active low pulse is 1us
+
+ io_set_pin(USBHUB_RESET);
+#endif // I2C_REWORK
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void power_signal_interrupt(void)
+{
+ io_set_pin(AVR_IRQ); // FIXME: Active low?
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if !defined(DEBUG) && !(defined(ENABLE_SERIAL) && defined(ATTINY88_DIP))
+static io_pin_t PS_POR = IO_PD(6);
+#define PS_POR_AVAILABLE
+#endif // DEBUG
+static io_pin_t PS_SRST = IO_PD(7);
+
+#define FPGA_RESET_DELAY 10 // ms // MAGIC
+
+void fpga_reset(bool delay)
+{
+#ifdef PS_POR_AVAILABLE
+ io_clear_pin(PS_POR);
+#endif // PS_POR_AVAILABLE
+ io_clear_pin(PS_SRST);
+
+ if (delay)
+ _delay_ms(FPGA_RESET_DELAY);
+#ifdef PS_POR_AVAILABLE
+ io_enable_pin(PS_POR, true);
+#endif // PS_POR_AVAILABLE
+ io_enable_pin(PS_SRST, true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static io_pin_t VBAT = IO_PC(0);
+
+void battery_init(void)
+{
+ //io_input_pin(VBAT);
+ DIDR0 |= 0x1; // Digital input disable PC0 (ADC0)
+
+ ADMUX = (1 << REFS0) // AVcc reference
+ | (0 << ADLAR) // Left-aligned result
+ | (0 << MUX0); // ADC0
+
+ ADCSRA = (0x7 << ADPS0);// Prescale clock by 128
+}
+
+uint16_t battery_get_voltage(void)
+{
+ // Vout = (357k / (274k + 357k)) * Vbat
+ // Vbat = (Vout * (274k + 357k)) / 357k
+
+ // ADC = (Vin * 1024) / Vref
+ // Vin = (ADC * Vref) / 1024
+ // Vref = 3.3
+
+ // Vbat(mV) = 1000 * (((ADC * 3.3) / 1024) * (274k + 357k)) / 357k
+ // Vbat(mV) ~= ADC * 5.70
+
+ ADCSRA |= (1 << ADEN); // FIXME: Turn on ADC (or leave on all the time?)
+
+ ADCSRA |= (1 << ADSC); // Start conversion
+
+ while (ADCSRA & (1 << ADSC)); // Wait for End of Conversion
+
+ /*uint16_t*/uint32_t voltage = (ADCH << 8) | (ADCL << 0);
+#ifdef ATTINY88_DIP
+ voltage = (voltage * 32227) / 10000; // ~3.22265625
+#else
+ voltage = (voltage * 56961) / 10000; // ~5.69606748
+#endif // ATTINY88_DIP
+ ADCSRA &= ~(1 << ADEN); // FIXME: Turn off ADC (or leave on all the time?)
+
+ return (uint16_t)voltage;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void blink_error_sequence(uint8_t len)
+{
+ charge_set_led(false);
+ _delay_ms(BLINK_ERROR_DELAY * 2);
+
+ for (; len > 0; len--) {
+ charge_set_led(true);
+ _delay_ms(BLINK_ERROR_DELAY);
+ charge_set_led(false);
+ _delay_ms(BLINK_ERROR_DELAY);
+ }
+
+ //for (len = 2; len > 0; len--) // Could have *2 on delay, but so as never to overflow 8-bit argument
+ // _delay_ms(BLINK_ERROR_DELAY);
+}
+
+typedef struct power_params {
+ power_subsystem_t subsys;
+ bool enable;
+ uint8_t retry;
+ //uint16_t opaque;
+} power_params_t;
+
+static bool _power_up_fpga(power_params_t* params)
+{
+ if (params->subsys != PS_FPGA)
+ return false;
+
+ if (params->enable == false)
+ {
+ //if (tps54478_is_power_good() == false) // Already off
+ // return true;
+
+ if (params->retry == 0)
+ {
+ io_clear_pin(PS_SRST); // FIXME: Hold it low to stop
+#ifdef PS_POR_AVAILABLE
+ io_clear_pin(PS_POR); // Prepare it for shutdown, and then the potential next power cycle
+#endif // PS_POR_AVAILABLE
+ tps54478_set_power(false);
+ }
+
+ //return (tps54478_is_power_good() == false);
+ return true;
+ }
+
+ //bool fpga_power_good = tps54478_is_power_good(); // TODO: Can it ever already be good?
+
+ if (params->retry == 0)
+ tps54478_set_power(true);
+
+ return tps54478_is_power_good();
+}
+
+static bool _power_up_reg(power_params_t* params)
+{
+ if ((params->subsys > PS_TX) || (params->subsys < PS_VDRAM))
+ return false;
+
+ struct reg_config* cfg = default_reg_config + params->subsys;
+
+ if (params->enable == false)
+ return ltc3675_enable_reg(cfg->address, false);
+
+ if (cfg->voltage > 0)
+ {
+ if (ltc3675_set_voltage(cfg->address, cfg->voltage) == false)
+ return false;
+ }
+
+ return ltc3675_enable_reg(cfg->address, true);
+}
+
+static bool _power_enable_subsys(power_params_t* params)
+{
+ switch (params->subsys)
+ {
+ case PS_FPGA:
+ return _power_up_fpga(params);
+ //case PS_:
+ // break;
+ default:
+ return _power_up_reg(params);
+ }
+
+ return false; // Should never get here
+}
+
+bool power_enable(power_subsystem_t subsys, bool on)
+{
+ power_params_t params;
+ ZERO_MEMORY(params);
+ params.subsys = subsys;
+ params.enable = on;
+
+ return _power_enable_subsys(&params);
+}
+
+typedef bool (*boot_function_t)(power_params_t*);
+
+struct boot_step {
+ power_subsystem_t subsys;
+ //boot_function_t fn;
+ //uint8_t delay;
+ //uint8_t retries;
+ //uint16_t opaque;
+ //bool powered;
+} boot_steps[] = { // MAGIC: Retries/delays
+ { PS_FPGA, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 7..8 // 3..4
+ { PS_VDRAM, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 9..10 // 5..6
+ { PS_PERIPHERALS_1_8, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 11..12 // 7..8
+ { PS_PERIPHERALS_3_3, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 13..14 // 9..10
+ { PS_TX, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ } // CHECK: Leaving TX off
+};
+/*
+bool power_is_subsys_on(int8_t index)
+{
+ if ((index < 0) || (index >= ARRAY_SIZE(boot_steps)))
+ return false;
+
+ struct boot_step* step = boot_steps + index;
+
+ return step->powered;
+}
+*/
+bool power_init(void)
+{
+ io_output_pin(CHARGE);
+#ifdef LED_POLARITY
+ io_output_pin(POWER_LED);
+#endif // LED_POLARITY
+
+ charge_set_led(true);
+
+ battery_init();
+
+ tps54478_init(true); // Will keep EN float (keep power on)
+#ifndef I2C_REWORK
+ i2c_init(PWR_SDA, PWR_SCL);
+
+ io_output_pin(USBHUB_RESET);
+#endif // I2C_REWORK
+#ifdef CHARGER_TI
+ if (bq24190_init(true) == false)
+ return false;
+#else
+ if (ltc4155_init(/*_state.battery_not_present*/true/*false*/) == false)
+ return false;
+#endif // CHARGER_TI
+#ifdef CHARGER_TI
+ _delay_ms(1000); // Still at 1.4V on dev board
+#else
+ _delay_ms(25); // Wait for charge current to stop (Vbatt to fall to 0V)
+#endif // CHARGER_TI
+ uint16_t batt_voltage = battery_get_voltage();
+ debug_log_ex("Vb ", false);
+ debug_log_byte((uint8_t)(batt_voltage / 100));
+ //debug_log_hex_ex(batt_voltage >> 8, false);
+ //debug_log_hex(batt_voltage & 0xFF);
+ if (batt_voltage < BATT_MIN_VOLTAGE)
+ {
+ _state.battery_not_present = true;
+
+ //debug_log("NoBatt");
+ }
+ else
+ {
+#ifdef CHARGER_TI
+ bq24190_toggle_charger(true);
+#else
+ ltc4155_set_charge_current_limit(50);
+#endif // CHARGER_TI
+ }
+
+ if (ltc3675_init(ltc3675_reg_helper) == false)
+ return false;
+#ifdef PS_POR_AVAILABLE
+ io_output_pin(PS_POR);
+#endif // PS_POR_AVAILABLE
+ io_output_pin(PS_SRST);
+ // Hold low until power is stable
+#ifdef PS_POR_AVAILABLE
+ io_clear_pin(PS_POR);
+#endif // PS_POR_AVAILABLE
+ io_clear_pin(PS_SRST);
+/*
+ AVR_CS
+ AVR_MOSI
+ AVR_MISO
+ AVR_SCK
+
+ FTDI_BCD
+ FTDI_PWREN2
+*/
+ io_input_pin(AVR_RESET); // Has external pull-up (won't do anything because this is configured at the hardware RESET pin)
+
+ //io_output_pin(AVR_IRQ); // Output here, input to FPGA
+ io_input_pin(AVR_IRQ);
+ //io_set_pin(AVR_IRQ); // FIXME: Active low?
+
+ ///////////////
+
+ EICRA = _BV(ISC01) | _BV(ISC00) | _BV(ISC10)/* | _BV(ISC11)*/; // Rising edge for INT0 (WAKEUP). [Falling for INT1.] Any logical change INT1 (ONSWITCH_DB)
+ //EIMSK = _BV(INT0); // [Turn on WAKEUP interrupt] Don't do this, as unit will turn on anyway
+ EIMSK = _BV(INT1) | _BV(INT0); // Turn on ONSWITCH_DB and WAKEUP
+
+ PCMSK0 = _BV(PCINT1) | _BV(PCINT0); // USBPM_IRQ | CORE_PGOOD
+ PCMSK2 = _BV(PCINT16)/* | _BV(PCINT20)*/; // PWR_IRQ/* | PWR_RESET*/
+ PCICR = _BV(PCIE2) | _BV(PCIE0);
+
+ ///////////////
+/*
+ TCNT0;
+ OCR0A = 0x;
+ TCCR0A = _BV(CTC0);
+ TIFR0;
+ TIMSK0;
+ TCCR0A |= 0x05; // Switch on with 1024 prescaler
+*/
+ TCCR1B = _BV(WGM12); // CTC mode
+ OCR1A = 15624 * 2; // Hold button for 2 seconds to switch off
+ TIMSK1 = _BV(OCIE1A); // Enable CTC on Timer 1
+
+ charge_set_led(false);
+
+ return true;
+}
+
+bool power_on(void)
+{
+ pmc_mask_irqs(true);
+
+ //charge_set_led(false);
+
+ bool last_power_led_state = /*true*/false;
+
+ //if ((ARRAY_SIZE(boot_steps) % 2) == 0) // Should end with 'true'
+ // last_power_led_state = false;
+
+ power_set_led(last_power_led_state);
+
+ uint8_t step_count, retry;
+ for (step_count = 0; step_count < ARRAY_SIZE(boot_steps); step_count++)
+ {
+ last_power_led_state = !last_power_led_state;
+ power_set_led(last_power_led_state);
+
+// debug_blink(step_count);
+// debug_blink(3 + (step_count * 2) + 0);
+// debug_blink_rev(7 + (step_count * 2) + 0);
+
+ struct boot_step* step = boot_steps + step_count;
+ if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN))
+ continue;
+
+ debug_log_ex("PWR ", false);
+ debug_log_byte_ex(step->subsys, true);
+
+ power_params_t params;
+
+ for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++)
+ {
+ ZERO_MEMORY(params);
+ params.subsys = step->subsys;
+ params.enable = true;
+ params.retry = retry;
+
+ if ((/*(step->fn != NULL) && (step->fn(&params))) ||
+ ((step->fn == NULL) && */(_power_enable_subsys(&params))))
+ {
+ //step->powered = true;
+ default_reg_config[step->subsys].powered = true;
+
+ debug_log("+");
+// debug_blink(3 + (step_count * 2) + 1);
+// debug_blink_rev(7 + (step_count * 2) + 1);
+
+//ltc4155_dump();
+
+ break;
+ }
+
+ debug_log("?");
+
+ if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/)
+ _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY);
+ }
+
+// debug_blink(step_count);
+
+ if (retry == /*step->retries*/POWER_DEFAULT_RETRIES)
+ break;
+ }
+
+ if (step_count != ARRAY_SIZE(boot_steps))
+ {
+ debug_log("x");
+
+ //sei(); // For button press detection
+
+ /*while (_state.powered == false) {
+ blink_error_sequence(step_count + BlinkError_FPGA_Power);
+ }*/
+ pmc_set_blink_error(step_count + BlinkError_FPGA_Power);
+
+ pmc_mask_irqs(false);
+
+ return false;
+ }
+
+ ///////////////////////////////////
+
+ //bool was_hub_in_reset = (io_is_pin_set(USBHUB_RESET) == false);
+
+ usbhub_reset();
+
+ /*if (was_hub_in_reset)
+ {
+ _delay_ms(10);
+ usbhub_reset();
+ }*/
+
+ fpga_reset(false); // Power has been brought up, so let FPGA run
+
+ ///////////////////////////////////
+
+// ltc4155_dump();
+
+ // Turn off WAKEUP interrupt, enable ONSWITCH_DB
+ //EIMSK = _BV(INT1);
+
+ _state.powered = true;
+//debug_blink_rev(1);
+//_delay_ms(1000); // Wait for FPGA PGOOD to stabilise
+ pmc_mask_irqs(false);
+
+ power_set_led(true);
+
+ if (_state.battery_charging)
+ {
+ _delay_ms(500*2);
+ charge_set_led(true);
+ }
+
+ return true;
+}
+
+uint8_t power_off(void)
+{
+ pmc_mask_irqs(true);
+
+ io_clear_pin(PS_SRST); // FIXME: Hold it low to stop FPGA running
+
+ bool last_power_led_state = /*false*/true;
+
+ //if ((ARRAY_SIZE(boot_steps) % 2) == 0) // Should end with 'false'
+ // last_power_led_state = true;
+
+ //power_set_led(last_power_led_state);
+
+ ///////////////////////////////////
+
+ int8_t step_count, retry;
+ for (step_count = ARRAY_SIZE(boot_steps) - 1; step_count >= 0; step_count--)
+ {
+ last_power_led_state = !last_power_led_state;
+ power_set_led(last_power_led_state);
+
+// debug_blink(step_count);
+
+ struct boot_step* step = boot_steps + step_count;
+ if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN))
+ continue;
+
+ power_params_t params;
+
+ for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++)
+ {
+ ZERO_MEMORY(params);
+ params.subsys = step->subsys;
+ params.enable = false;
+ params.retry = retry;
+
+ if ((/*(step->fn != NULL) && (step->fn(&params))) ||
+ ((step->fn == NULL) && */(_power_enable_subsys(&params))))
+ {
+ //step->powered = false;
+ default_reg_config[step->subsys].powered = false;
+ break;
+ }
+
+ if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/)
+ _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY);
+ }
+
+// debug_blink(step_count);
+
+ if (retry == /*step->retries*/POWER_DEFAULT_RETRIES)
+ break;
+ }
+
+ if (step_count != -1)
+ {
+ /*pmc_mask_irqs(false);
+
+ while (_state.powered) {
+ blink_error_sequence(step_count + BlinkError_FPGA_Power);
+ }*/
+ if (pmc_get_blink_error() == BlinkError_None) // Only set blink error if no existing error
+ pmc_set_blink_error(step_count + BlinkError_FPGA_Power);
+
+ pmc_mask_irqs(false);
+
+ return (step_count + 1);
+ }
+
+ ///////////////////////////////////
+
+ // Turn off WAKEUP interrupt, enable ONSWITCH_DB
+ //EIMSK = _BV(INT1);
+
+ _state.powered = false;
+
+ pmc_mask_irqs(false);
+
+ power_set_led_ex(false, false);
+ _delay_ms(500*2);
+
+ power_set_led(false); // Will turn on charger LED if battery is charging
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef DEBUG
+
+#ifdef ATTINY88_DIP
+static io_pin_t DEBUG_1 = IO_PB(6);
+static io_pin_t DEBUG_2 = IO_PB(7);
+#endif // ATTINY88_DIP
+
+#endif // DEBUG
+
+ISR(INT0_vect) // PD(2) WAKEUP: Rising edge
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ //power_on();
+ debug_log("\nINT0\n");
+ _state.wake_up = true;
+
+ //sei();
+ pmc_mask_irqs(false);
+}
+
+ISR(INT1_vect) // PD(3) ONSWITCH_DB (PB_STAT): Any change
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ if (ltc3675_is_power_button_depressed())
+ {
+ debug_log("PWRBTN+");
+
+ TCNT1 = 0;
+ if ((TCCR1B & 0x07) == 0x00)
+ {
+ _state.active_timers++;
+ debug_log("TIMER1+");
+ }
+ TCCR1B |= /*0x5*/0x3; // [1024] 64 prescaler
+ //_state.timers_running = true;
+
+ //debug_set(DEBUG_1, true);
+ //debug_set(DEBUG_2, false);
+ }
+ else
+ {
+ debug_log("PWRBTN-");
+
+ //if (TIMSK1 & _BV(OCIE1A)) // If letting go of button and still running, stop timer
+ {
+ //TIMSK1 &= ~_BV(OCIE1A);
+ if ((TCCR1B & 0x07) != 0x00)
+ {
+ _state.active_timers--;
+ debug_log("TIMER1-");
+ }
+ TCCR1B &= ~0x7; // Disable timer
+ //_state.timers_running = false;
+
+ //debug_set(DEBUG_1, false);
+ }
+ }
+
+ //sei();
+ pmc_mask_irqs(false);
+}
+
+ISR(TIMER1_COMPA_vect)
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ debug_log("TIMER1");
+
+ //TIMSK1 &= ~_BV(OCIE1A); // Turn off timer
+ TCCR1B &= ~0x7; // Disable timer
+ //_state.timers_running = false;
+ _state.active_timers--;
+
+ if (_state.powered)
+ {
+ debug_log("PWROFF");
+
+ _state.power_off = true;
+ }
+
+ //debug_set(DEBUG_2, true);
+
+ //power_off();
+
+ //sei();
+ pmc_mask_irqs(false);
+
+ //sleep_mode();
+}
+
+ISR(PCINT0_vect)
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ //debug_log("PCINT0");
+
+ // CORE_PGOOD
+ // Assert low: power problem -> shutdown
+ // USBPM_IRQ
+ // Charge status change? -> update LED
+ // Power problem: battery -> blink charge LED
+ // major -> shutdown
+
+ if (/*(_state.powered) && */(/*io_test_pin(CORE_PGOOD)*/tps54478_is_power_good() == false))
+ {
+ _state.core_power_bad = true;
+ }
+#ifdef CHARGER_TI
+ if (bq24190_has_interrupt())
+ {
+ _state.bq24190_irq = true;
+ }
+#else
+ if (ltc4155_has_interrupt())
+ {
+ _state.ltc4155_irq = true;
+ }
+#endif // CHARGER_TI
+ //sei();
+ pmc_mask_irqs(false);
+}
+
+ISR(PCINT2_vect)
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ //debug_log("PCINT2");
+
+ // PWR_IRQ
+ // Regulator problem: shutdown
+ // PWR_RESET
+ // Ignored
+
+ if (ltc3675_has_interrupt())
+ {
+ //debug_set(IO_PB(6), true);
+ _state.ltc3675_irq = true;
+ }
+
+ //sei();
+ pmc_mask_irqs(false);
+}
diff --git a/firmware/e300/rev_b/power.h b/firmware/e300/rev_b/power.h
new file mode 100644
index 000000000..453633414
--- /dev/null
+++ b/firmware/e300/rev_b/power.h
@@ -0,0 +1,58 @@
+#ifndef POWER_H
+#define POWER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+void tps54478_init(bool enable);
+void tps54478_set_power(bool on); // Zynq core power (1.0V for FPGA)
+bool tps54478_is_power_good(void);
+
+void charge_set_led(bool on); // Here for error blink codes
+void charge_notify(bool charging);
+
+void power_signal_interrupt(void);
+
+void fpga_reset(bool delay);
+
+typedef enum power_subsystems {
+ PS_UNKNOWN,
+ PS_FPGA,
+ PS_VDRAM,
+ PS_PERIPHERALS_1_8,
+ PS_PERIPHERALS_3_3,
+ PS_TX,
+ PS_MAX
+} power_subsystem_t;
+
+enum Regulators
+{
+ REG_UNKNOWN,
+ REG_TPS54478,
+ REG_LTC3675
+};
+
+bool power_enable(power_subsystem_t subsys, bool on);
+
+void battery_init(void);
+uint16_t battery_get_voltage(void); // mV
+
+bool power_init(void);
+bool power_on(void);
+uint8_t power_off(void);
+
+//bool power_is_subsys_on(int8_t index);
+bool power_is_subsys_on(power_subsystem_t index);
+//int8_t power_get_regulator_index(uint8_t device, uint8_t address);
+//bool ltc3675_reg_helper(uint8_t address);
+
+void usbhub_reset(void);
+
+#ifndef I2C_REWORK
+#include "io.h"
+
+extern io_pin_t PWR_SDA;
+extern io_pin_t PWR_SCL;
+#endif // I2C_REWORK
+
+#endif // POWER_H
diff --git a/firmware/e300/rev_c/Makefile b/firmware/e300/rev_c/Makefile
new file mode 100644
index 000000000..0f13dc60b
--- /dev/null
+++ b/firmware/e300/rev_c/Makefile
@@ -0,0 +1,76 @@
+#
+# Copyright 2009 Ettus Research LLC
+#
+
+##################################################
+# Compiler
+##################################################
+CC = avr-gcc
+OBJCOPY = avr-objcopy
+STRIP = avr-strip
+OBJDUMP = avr-objdump
+SREC = srec_cat
+CFLAGS = -Os -std=gnu99 -Wall -fshort-enums -pedantic-errors -Wl,--gc-sections \
+ -Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow \
+ -DENABLE_SERIAL -DCHARGER_TI -DLED_POLARITY -DDEBUG_VOID
+# -DENABLE_SERIAL : Output serial debug
+# -DCHARGER_TI : Use TI charger (rev B) instead of LTC (rev A)
+# -DLED_POLARITY : Dual-polarity LED on rev B
+# -DDDR3L : Lower DDR voltage (rev B R-divider changed, so disable this to get back into nominal range)
+# -DDEBUG_VOID : Use (void) debug function replacements instead of inline NOOP (use if .text overflows)
+# -DI2C_REWORK : Rev A only
+# -DDEBUG : Enable debug routines (LED blinks, etc)
+# -DDEBUG_SAFETY : Extra debug prints
+# -DATTINY88_DIP : ATTINY88 DIP testing on STK600
+# -DHARDWIRE_ENABLE : LTC3675 dedicated enable lines - don't use
+
+#-Werror
+#-D IO_DEBUG
+
+##################################################
+# Files
+##################################################
+HDRS =
+SRCS = main.c io.c power.c ltc3675.c i2c.c debug.c bq24190.c
+TARGET = main
+
+##################################################
+# Device
+##################################################
+MMCU = attiny88
+#PROGRAMMER = avrisp2
+PROGRAMMER = stk600
+PORT = usb
+AVRDUDE = avrdude -p $(MMCU) -c $(PROGRAMMER) -P $(PORT)
+
+##################################################
+# Global Targets
+##################################################
+all: $(TARGET).hex
+
+clean:
+ $(RM) *.o *.elf *.hex
+
+install: all
+ $(AVRDUDE) -U flash:w:$(TARGET).hex:i
+
+##################################################
+# Dependency Targets
+##################################################
+fuses.hex: $(TARGET).elf
+ $(OBJCOPY) -j .fuse -O ihex $< $@ --change-section-lma .fuse=0
+
+lfuse.hex: fuses.hex
+ $(SREC) $< -Intel -crop 0x00 0x01 -offset 0x00 -O $@ -Intel
+
+hfuse.hex: fuses.hex
+ $(SREC) $< -Intel -crop 0x01 0x02 -offset -0x01 -O $@ -Intel
+
+$(TARGET).hex: $(TARGET).elf
+ $(OBJCOPY) -R .eeprom -R .fuse -O ihex $< $@
+
+$(TARGET).elf: $(SRCS:.c=.o)
+ $(CC) -mmcu=$(MMCU) $^ -o $@
+
+%.o: %.c $(HDRS) Makefile
+ $(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS)
diff --git a/firmware/e300/rev_c/PMC.atsln b/firmware/e300/rev_c/PMC.atsln
new file mode 100644
index 000000000..e9f4ffc79
--- /dev/null
+++ b/firmware/e300/rev_c/PMC.atsln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Atmel Studio Solution File, Format Version 11.00
+Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "PMC", "PMC.cproj", "{A379D421-4236-44AF-A711-52B7BDA29919}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|AVR = Debug|AVR
+ Release (DDR3L)|AVR = Release (DDR3L)|AVR
+ Release (DDR3L, Ti)|AVR = Release (DDR3L, Ti)|AVR
+ Release (Dev)|AVR = Release (Dev)|AVR
+ Release|AVR = Release|AVR
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.ActiveCfg = Debug|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.Build.0 = Debug|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.ActiveCfg = Release (DDR3L)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.Build.0 = Release (DDR3L)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.ActiveCfg = Release (DDR3L, Ti)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.Build.0 = Release (DDR3L, Ti)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.ActiveCfg = Release (Dev)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.Build.0 = Release (Dev)|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.ActiveCfg = Release|AVR
+ {A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.Build.0 = Release|AVR
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/firmware/e300/rev_c/PMC.cproj b/firmware/e300/rev_c/PMC.cproj
new file mode 100644
index 000000000..e3784d10b
--- /dev/null
+++ b/firmware/e300/rev_c/PMC.cproj
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectVersion>6.0</ProjectVersion>
+ <ToolchainName>com.Atmel.AVRGCC8</ToolchainName>
+ <ProjectGuid>{a379d421-4236-44af-a711-52b7bda29919}</ProjectGuid>
+ <avrdevice>ATtiny88</avrdevice>
+ <avrdeviceseries>none</avrdeviceseries>
+ <OutputType>Executable</OutputType>
+ <Language>C</Language>
+ <OutputFileName>$(MSBuildProjectName)</OutputFileName>
+ <OutputFileExtension>.elf</OutputFileExtension>
+ <OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
+ <AssemblyName>PMC</AssemblyName>
+ <Name>PMC</Name>
+ <RootNamespace>PMC</RootNamespace>
+ <ToolchainFlavour>Native</ToolchainFlavour>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <OverrideVtorValue />
+ <eraseonlaunchrule>0</eraseonlaunchrule>
+ <AsfVersion>3.1.3</AsfVersion>
+ <avrtool>com.atmel.avrdbg.tool.stk600</avrtool>
+ <avrtoolinterface>ISP</avrtoolinterface>
+ <com_atmel_avrdbg_tool_stk600>
+ <ToolType>com.atmel.avrdbg.tool.stk600</ToolType>
+ <ToolName>STK600</ToolName>
+ <ToolNumber>004A8D68669B</ToolNumber>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <OverrideVtorValue>
+ </OverrideVtorValue>
+ <Channel>
+ <host>127.0.0.1</host>
+ <port>49172</port>
+ <ssl>False</ssl>
+ </Channel>
+ <ToolOptions>
+ <InterfaceName>ISP</InterfaceName>
+ <InterfaceProperties>
+ <JtagDbgClock>249000</JtagDbgClock>
+ <JtagProgClock>1000000</JtagProgClock>
+ <IspClock>25000</IspClock>
+ <JtagInChain>false</JtagInChain>
+ <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession>
+ <JtagDevicesBefore>0</JtagDevicesBefore>
+ <JtagDevicesAfter>0</JtagDevicesAfter>
+ <JtagInstrBitsBefore>0</JtagInstrBitsBefore>
+ <JtagInstrBitsAfter>0</JtagInstrBitsAfter>
+ </InterfaceProperties>
+ </ToolOptions>
+ </com_atmel_avrdbg_tool_stk600>
+ <com_atmel_avrdbg_tool_ispmk2>
+ <ToolType>com.atmel.avrdbg.tool.ispmk2</ToolType>
+ <ToolName>AVRISP mkII</ToolName>
+ <ToolNumber>000200136505</ToolNumber>
+ <KeepTimersRunning>true</KeepTimersRunning>
+ <OverrideVtor>false</OverrideVtor>
+ <OverrideVtorValue>
+ </OverrideVtorValue>
+ <Channel>
+ <host>127.0.0.1</host>
+ <port>49228</port>
+ <ssl>False</ssl>
+ </Channel>
+ <ToolOptions>
+ <InterfaceName>ISP</InterfaceName>
+ <InterfaceProperties>
+ <JtagDbgClock>249000</JtagDbgClock>
+ <JtagProgClock>1000000</JtagProgClock>
+ <IspClock>8000</IspClock>
+ <JtagInChain>false</JtagInChain>
+ <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession>
+ <JtagDevicesBefore>0</JtagDevicesBefore>
+ <JtagDevicesAfter>0</JtagDevicesAfter>
+ <JtagInstrBitsBefore>0</JtagInstrBitsBefore>
+ <JtagInstrBitsAfter>0</JtagInstrBitsAfter>
+ </InterfaceProperties>
+ </ToolOptions>
+ </com_atmel_avrdbg_tool_ispmk2>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK</Value>
+ <Value>ENABLE_SERIAL</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ </AvrGcc>
+ </ToolchainSettings>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>ATTINY88_DIP</Value>
+ <Value>DEBUG</Value>
+ <Value>I2C_REWORK</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ <avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel>
+ </AvrGcc>
+ </ToolchainSettings>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L)' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK</Value>
+ <Value>ENABLE_SERIAL</Value>
+ <Value>DDR3L</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ </AvrGcc>
+ </ToolchainSettings>
+ <OutputPath>bin\Release (DDR3)\</OutputPath>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release (Dev)' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK</Value>
+ <Value>ENABLE_SERIAL</Value>
+ <Value>ATTINY88_DIP</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection>True</avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+ <avrgcc.linker.optimization.GarbageCollectUnusedSections>True</avrgcc.linker.optimization.GarbageCollectUnusedSections>
+ </AvrGcc>
+ </ToolchainSettings>
+ <OutputPath>bin\Release (Dev)\</OutputPath>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L, Ti)' ">
+ <ToolchainSettings>
+ <AvrGcc>
+ <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
+ <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
+ <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
+ <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
+ <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
+ <avrgcc.compiler.symbols.DefSymbols>
+ <ListValues>
+ <Value>I2C_REWORK_disabled</Value>
+ <Value>DDR3L_disabled_revB_R_FB_network_changed</Value>
+ <Value>CHARGER_TI</Value>
+ <Value>ENABLE_SERIAL_disabled</Value>
+ <Value>LED_POLARITY</Value>
+ </ListValues>
+ </avrgcc.compiler.symbols.DefSymbols>
+ <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
+ <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
+ <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
+ <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
+ <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors>
+ <avrgcc.linker.libraries.Libraries>
+ <ListValues>
+ <Value>m</Value>
+ </ListValues>
+ </avrgcc.linker.libraries.Libraries>
+</AvrGcc>
+ </ToolchainSettings>
+ <OutputPath>bin\Release (DDR3L, Ti)\</OutputPath>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="bq24190.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="bq24190.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="config.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="debug.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="debug.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="error.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="global.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="i2c.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="i2c.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="io.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="io.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc3675.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc3675.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc4155.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="ltc4155.h">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="main.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="power.c">
+ <SubType>compile</SubType>
+ </Compile>
+ <Compile Include="power.h">
+ <SubType>compile</SubType>
+ </Compile>
+ </ItemGroup>
+ <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
+</Project> \ No newline at end of file
diff --git a/firmware/e300/rev_c/bq24190.c b/firmware/e300/rev_c/bq24190.c
new file mode 100644
index 000000000..029beacf6
--- /dev/null
+++ b/firmware/e300/rev_c/bq24190.c
@@ -0,0 +1,335 @@
+/*
+ * bq24190.c
+ *
+ * Created: 11/12/2012 4:58:12 PM
+ * Author: Balint Seeber
+ */
+
+#ifdef CHARGER_TI
+
+#include "config.h"
+#include "bq24190.h"
+
+#include <util/delay.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+#ifndef I2C_REWORK
+#include "power.h"
+#endif // I2C_REWORK
+
+static io_pin_t USBPM_IRQ = IO_PB(1);
+
+#ifdef ATTINY88_DIP
+
+static io_pin_t CHRG_SDA = IO_PC(2);
+static io_pin_t CHRG_SCL = IO_PC(3);
+
+#else
+
+#ifdef I2C_REWORK
+
+static io_pin_t CHRG_SDA = IO_PC(4);
+static io_pin_t CHRG_SCL = IO_PC(5);
+
+#else
+
+#define CHRG_SDA PWR_SDA
+#define CHRG_SCL PWR_SCL
+
+#endif // I2C_REWORK
+
+#endif // ATTINY88_DIP
+
+const bool _bq24190_pull_up = false;
+
+#define BQ24190_BASE_ADDRESS (0x6B << 1)
+#define BQ24190_WRITE_ADDRESS (BQ24190_BASE_ADDRESS + 0)
+#define BQ24190_READ_ADDRESS (BQ24190_BASE_ADDRESS + 1)
+
+enum BQ24190Registers
+{
+ BQ24190_REG_INPUT_SOURCE_CTL= 0,
+ BQ24190_REG_PWR_ON_CONFIG = 1,
+ BQ24190_REG_CHARGE_CURRENT = 2,
+ BQ24190_REG_PRE_TERM_CURRENT= 3,
+ BQ24190_REG_CHARGE_VOLTAGE = 4,
+ BQ24190_REG_TIMER_CONTROL = 5,
+ BQ24190_REG_SYSTEM_STATUS = 8,
+ BQ24190_REG_FAULT = 9
+};
+/*
+enum BQ24190TimerControl
+{
+
+};
+*/
+enum BQ24190Shifts
+{
+ BQ24190_SHIFTS_CHARGER_CONFIG = 4,
+ BQ24190_SHIFTS_I2C_WATCHDOG = 4,
+ BQ24190_SHIFTS_CHARGER_STATUS = 4,
+ BQ24190_SHIFTS_CHARGER_FAULT = 4,
+};
+
+enum BQ24190VBusStatus
+{
+ BQ24190_VBUS_UNKNOWN,
+ BQ24190_VBUS_USB,
+ BQ24190_VBUS_ADAPTER,
+ BQ24190_VBUS_OTG
+};
+
+enum BQ24190ChargerStatus
+{
+ BQ24190_CHRG_STAT_NOT_CHARGING,
+ BQ24190_CHRG_STAT_PRE_CHARGE,
+ BQ24190_CHRG_STAT_FAST_CHARGING,
+ BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE,
+ BQ24190_CHRG_STAT_MASK = BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE
+};
+
+enum BQ24190SystemStatus
+{
+ BQ24190_STATUS_DPM = 0x08,
+ BQ24190_STATUS_POWER_GOOD = 0x04,
+ BQ24190_STATUS_THERMAL_REGULATION = 0x02,
+ BQ24190_STATUS_VSYSMIN_REGULATION = 0x01
+};
+
+enum BQ24190Faults
+{
+ BQ24190_FAULT_WATCHDOG_EXPIRED = 0x80,
+ BQ24190_FAULT_VBUS_OVERLOADED = 0x40,
+ BQ24190_FAULT_BATOVP = 0x08
+};
+
+enum BQ24190ChargerFaults
+{
+ BQ24190_CHRGFAULT_NORMAL,
+ BQ24190_CHRGFAULT_INPUT,
+ BQ24190_CHRGFAULT_THERMAL,
+ BQ24190_CHRGFAULT_SAFETY_TIMER
+};
+
+enum BQ24190NTCFaults
+{
+ BQ24190_NTCFAULT_NORMAL,
+ BQ24190_NTCFAULT_TS1_COLD,
+ BQ24190_NTCFAULT_TS1_HOT,
+ BQ24190_NTCFAULT_TS2_COLD,
+ BQ24190_NTCFAULT_TS2_HOT,
+ BQ24190_NTCFAULT_BOTH_COLD,
+ BQ24190_NTCFAULT_BOTH_HOT
+};
+
+bool bq24190_toggle_charger(bool on)
+{
+ uint8_t config = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQPC ", false);
+ debug_log_hex(config);
+
+ config &= ~(0x3 << BQ24190_SHIFTS_CHARGER_CONFIG);
+ if (on)
+ config |= (0x01 << BQ24190_SHIFTS_CHARGER_CONFIG); // Enable charger
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, config, _bq24190_pull_up) == false)
+ return false;
+
+ ////////
+/*
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQPC ", false);
+ debug_log_hex(config);
+*/
+ ////////
+
+ return true;
+}
+
+bool bq24190_init(bool disable_charger)
+{
+#ifdef I2C_REWORK
+ i2c_init_ex(CHRG_SDA, CHRG_SCL, _bq24190_pull_up);
+#endif // I2C_REWORK
+ io_input_pin(USBPM_IRQ);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP)
+ //io_set_pin(USBPM_IRQ); // [Enable pull-up for Open Drain] AVR pull-up not enough
+#endif // DEBUG
+ if (disable_charger)
+ {
+ if (bq24190_toggle_charger(false) == false)
+ return false;
+ }
+
+ ///////////////////////////////////
+
+ uint8_t timer_control = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_TIMER_CONTROL, &timer_control, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQTC ", false);
+ debug_log_hex(timer_control);
+
+ timer_control &= ~(0x3 << BQ24190_SHIFTS_I2C_WATCHDOG);
+ timer_control |= (0x00 << BQ24190_SHIFTS_I2C_WATCHDOG); // Disable I2C watch dog
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_TIMER_CONTROL, timer_control, _bq24190_pull_up) == false)
+ return false;
+
+ ///////////////////////////////////
+
+ //BQ24190_REG_PWR_ON_CONFIG
+ // Minimum System Voltage Limit: (default) 101 3.5V
+
+ //BQ24190_REG_CHARGE_CURRENT
+ // Fast Charge Current Limit: (default) 011000 2048mA
+
+ //BQ24190_REG_PRE_TERM_CURRENT
+ // Pre-charge current limit: (default) 0001 256mA
+ // Termination current limit: (default) 0001 256mA
+
+ //BQ24190_REG_CHARGE_VOLTAGE
+ // Charge voltage limit: (default) 101100 4.208V
+
+ ///////////////////////////////////
+
+ uint8_t input_src_ctl = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, &input_src_ctl, _bq24190_pull_up) == false)
+ return false;
+
+ debug_log_ex("BQIS ", false);
+ debug_log_hex(input_src_ctl);
+
+ // Input voltage limit: (default) 0110 4.36V
+
+ //input_src_ctl &= ~(0x07);
+ input_src_ctl |= (0x07); // Set 3A limit
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, input_src_ctl, _bq24190_pull_up) == false)
+ return false;
+
+ return true;
+}
+
+bool bq24190_has_interrupt(void)
+{
+ //bool state = io_test_pin(USBPM_IRQ);
+ //debug_log_ex("BQIRQ", false);
+ //debug_log_byte(state);
+ //return (state != 1);
+ return (io_test_pin(USBPM_IRQ) == false);
+}
+
+static uint8_t _bq24190_last_status, _bq24190_last_fault;
+
+bool _bq24190_handle_irq(void)
+{
+ uint8_t val = 0x00;
+ bool result = false;
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ debug_log_ex("BQST ", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ _bq24190_last_status = val;
+
+ debug_log_ex("BQST ", false);
+ debug_log_hex(val);
+
+ /*if (val & LTC4155_WALLSNS_GOOD)
+ {
+ uint8_t wall_state = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ wall_state &= ~0x1E;
+ wall_state |= 0x0E;
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ debug_log("I+");
+ }*/
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ debug_log_ex("BQF ", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false)
+ goto _bq24190_handle_fail;
+
+ _bq24190_last_fault = val;
+
+ debug_log_ex("BQF ", false);
+ debug_log_hex(val);
+
+ val = (_bq24190_last_status >> BQ24190_SHIFTS_CHARGER_STATUS) & BQ24190_CHRG_STAT_MASK;
+
+ if (_state.blink_error == BlinkError_None)
+ {
+ switch (val)
+ {
+ case BQ24190_CHRG_STAT_PRE_CHARGE:
+ case BQ24190_CHRG_STAT_FAST_CHARGING:
+ //case BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE:
+ {
+ if ((_state.battery_not_present == false)/* &&
+ (_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD))*/)
+ {
+ //charge_set_led(true);
+ charge_notify(true);
+ break;
+ }
+ }
+ //case BQ24190_CHRG_STAT_NOT_CHARGING:
+ default:
+ //charge_set_led(false);
+ charge_notify(false);
+ }
+ }
+
+// bq24190_dump();
+
+ result = true;
+_bq24190_handle_fail:
+ return result;
+}
+
+bool bq24190_handle_irq(void) // IRQ is pulsed (not held)
+{
+ pmc_mask_irqs(true);
+
+ //_delay_ms(250); // [Wait for registers to update]
+
+ bool result = _bq24190_handle_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+//void bq24190_dump(void)
+/*
+bool bq24190_set_charge_current_limit(uint8_t deciamps)
+{
+ return true;
+}
+*/
+#endif // CHARGER_TI
diff --git a/firmware/e300/rev_c/bq24190.h b/firmware/e300/rev_c/bq24190.h
new file mode 100644
index 000000000..dc20ca796
--- /dev/null
+++ b/firmware/e300/rev_c/bq24190.h
@@ -0,0 +1,26 @@
+/*
+ * bq24190.h
+ *
+ * Created: 11/12/2012 4:58:23 PM
+ * Author: Balint Seeber
+ */
+
+
+#ifndef BQ24190_H_
+#define BQ24190_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef CHARGER_TI
+
+bool bq24190_init(bool disable_charger);
+bool bq24190_has_interrupt(void);
+bool bq24190_handle_irq(void);
+//void bq24190_dump(void);
+//bool bq24190_set_charge_current_limit(uint8_t deciamps);
+bool bq24190_toggle_charger(bool on);
+
+#endif // !CHARGER_TI
+
+#endif /* BQ24190_H_ */
diff --git a/firmware/e300/rev_c/config.h b/firmware/e300/rev_c/config.h
new file mode 100644
index 000000000..2547f65f0
--- /dev/null
+++ b/firmware/e300/rev_c/config.h
@@ -0,0 +1,2 @@
+#define __DELAY_BACKWARD_COMPATIBLE__ // Avoid compile-time arg error to '__builtin_avr_delay_cycles'
+#define F_CPU 1000000UL // 1 MHz (8MHz / 8)
diff --git a/firmware/e300/rev_c/debug.c b/firmware/e300/rev_c/debug.c
new file mode 100644
index 000000000..ff7d09445
--- /dev/null
+++ b/firmware/e300/rev_c/debug.c
@@ -0,0 +1,297 @@
+/*
+ * debug.c
+ */
+
+#include "config.h"
+#include "debug.h"
+
+#include <util/delay.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "io.h"
+#include "power.h"
+#include "global.h"
+
+#define DEBUG_BLINK_DELAY 250 // ms
+
+#ifdef ATTINY88_DIP
+
+#define SERIAL_DEBUG_INDEX 6
+#define SERIAL_DEBUG_PORT PORTD
+static io_pin_t SERIAL_DEBUG = IO_PD(SERIAL_DEBUG_INDEX);
+
+#else
+/*
+#ifdef I2C_REWORK
+//static io_pin_t SERIAL_DEBUG = IO_PC(1); // EN1
+#else
+//static io_pin_t SERIAL_DEBUG = EN4;
+#endif // I2C_REWORK
+*/
+// No good: PWR_EN4 trace still connected to LTC3675
+//#define SERIAL_DEBUG_INDEX 1
+//#define SERIAL_DEBUG_PORT PORTA
+//static io_pin_t SERIAL_DEBUG = IO_PA(SERIAL_DEBUG_INDEX);
+
+// AVR_MISO
+#define SERIAL_DEBUG_INDEX 4
+#define SERIAL_DEBUG_PORT PORTB
+static io_pin_t SERIAL_DEBUG = IO_PB(SERIAL_DEBUG_INDEX);
+
+#endif // ATTINY88_DIP
+/*
+#ifdef DEBUG
+
+#else
+
+#endif // DEBUG
+*/
+#ifdef DEBUG
+
+#ifdef ATTINY88_DIP
+static io_pin_t DEBUG_1 = IO_PB(6);
+static io_pin_t DEBUG_2 = IO_PB(7);
+#endif // ATTINY88_DIP
+
+void debug_init()
+{
+ io_output_pin(DEBUG_1);
+ io_output_pin(DEBUG_2);
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+#ifdef ENABLE_SERIAL
+ io_set_pin(SERIAL_DEBUG);
+ io_output_pin(SERIAL_DEBUG);
+#endif // ENABLE_SERIAL
+}
+
+#else
+
+void debug_init()
+{
+#ifdef ENABLE_SERIAL
+ io_set_pin(SERIAL_DEBUG);
+ io_output_pin(SERIAL_DEBUG);
+#endif // ENABLE_SERIAL
+}
+
+#endif // DEBUG
+
+#if defined(DEBUG) && !defined(DEBUG_VOID)
+
+void debug_set(io_pin_t pin, bool enable)
+{
+ io_enable_pin(pin, !enable);
+}
+
+void debug_blink(uint8_t count)
+{
+ io_enable_pin(DEBUG_1, false);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+
+ for (; count > 0; count--) {
+ io_enable_pin(DEBUG_2, false);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ }
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+}
+
+void debug_blink_rev(uint8_t count)
+{
+ io_enable_pin(DEBUG_2, false);
+ io_enable_pin(DEBUG_1, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+
+ for (; count > 0; count--) {
+ io_enable_pin(DEBUG_1, false);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ io_enable_pin(DEBUG_1, true);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ }
+
+ io_enable_pin(DEBUG_2, true);
+ io_enable_pin(DEBUG_1, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+}
+
+void debug_blink2(uint8_t count)
+{
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+
+ bool b = false;
+ for (; count > 0; count--) {
+ io_enable_pin(DEBUG_1, b);
+ io_enable_pin(DEBUG_2, b);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ b = !b;
+ }
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+}
+
+void debug_wait(void)
+{
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+
+ bool b = false;
+ while (true)
+ {
+ io_enable_pin(DEBUG_1, b);
+ io_enable_pin(DEBUG_2, !b);
+
+ _delay_ms(DEBUG_BLINK_DELAY);
+
+ b = !b;
+ }
+
+ io_enable_pin(DEBUG_1, true);
+ io_enable_pin(DEBUG_2, true);
+}
+
+#else
+
+#ifndef DEBUG_VOID
+
+void debug_blink_rev(uint8_t count)
+{
+ charge_set_led(true);
+ _delay_ms(DEBUG_BLINK_DELAY * 4);
+
+ for (; count > 0; count--) {
+ charge_set_led(false);
+ _delay_ms(DEBUG_BLINK_DELAY);
+ charge_set_led(true);
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+ }
+
+ _delay_ms(DEBUG_BLINK_DELAY * 2);
+ charge_set_led(false);
+ _delay_ms(DEBUG_BLINK_DELAY * 4);
+}
+#endif // DEBUG_VOID
+
+#endif // DEBUG
+
+#ifdef ENABLE_SERIAL
+
+static void _serial_tx(uint8_t* buffer)
+{
+ //uint8_t time_fix = 0;
+ // 3333/2 - 10
+ // 650
+ // [-2 for DEV] -20 works (perhaps different USB-Serial converter)
+ // [-20 for PRD] Which board?
+ // +20 Board #5 (-0: 3.592, -10: 3.280)
+ const uint16_t delay = 650+20;
+ uint16_t countdown;
+
+ for (uint8_t j = 0; j < 10; ++j)
+ {
+ if (buffer[j])
+ SERIAL_DEBUG_PORT |= _BV(SERIAL_DEBUG_INDEX);
+ else
+ SERIAL_DEBUG_PORT &= ~_BV(SERIAL_DEBUG_INDEX);
+
+ countdown = delay;
+ while (--countdown)
+ __asm("nop");
+ }
+}
+
+static void _serial_tx_char(char c)
+{
+ uint8_t buffer[10];
+ uint8_t i = 0;
+
+ buffer[i++] = 0; // START
+ for (int idx = 0; idx < 8; ++idx)
+ buffer[i++] = (((uint8_t)(c) & ((uint8_t)1<<((idx)))) ? 0x01 : 0x00); // Endianness: 7-
+ buffer[i++] = 1; // STOP
+
+ _serial_tx(buffer);
+}
+
+void debug_log_ex_P(const char* message, bool new_line)
+{
+ char c = pgm_read_byte(message);
+ if (c == '\0')
+ return;
+
+ pmc_mask_irqs(true);
+
+ do
+ {
+ _serial_tx_char(c);
+ c = pgm_read_byte(++message);
+ } while (c != '\0');
+
+ if (new_line)
+ _serial_tx_char('\n');
+
+ io_set_pin(SERIAL_DEBUG);
+
+ pmc_mask_irqs(false);
+}
+
+void _debug_log_ex(const char* message, bool new_line)
+{
+ if (message[0] == '\0')
+ return;
+
+ pmc_mask_irqs(true);
+
+ do
+ {
+ _serial_tx_char(*message);
+ } while (*(++message) != '\0');
+
+ if (new_line)
+ _serial_tx_char('\n');
+
+ io_set_pin(SERIAL_DEBUG);
+
+ pmc_mask_irqs(false);
+}
+
+void debug_log_byte_ex(uint8_t n, bool new_line)
+{
+ char ch[4];
+ ch[0] = '0' + (n / 100);
+ ch[1] = '0' + ((n % 100) / 10);
+ ch[2] = '0' + (n % 10);
+ ch[3] = '\0';
+ _debug_log_ex(ch, new_line);
+}
+
+void debug_log_hex_ex(uint8_t n, bool new_line)
+{
+ char ch[4];
+ ch[0] = 'x';
+ uint8_t _n = n >> 4;
+ if (_n < 10)
+ ch[1] = '0' + _n;
+ else
+ ch[1] = 'A' + (_n - 10);
+ n &= 0x0F;
+ if (n < 10)
+ ch[2] = '0' + n;
+ else
+ ch[2] = 'A' + (n - 10);
+ ch[3] = '\0';
+ _debug_log_ex(ch, new_line);
+}
+
+#endif // ENABLE_SERIAL
diff --git a/firmware/e300/rev_c/debug.h b/firmware/e300/rev_c/debug.h
new file mode 100644
index 000000000..5e6435972
--- /dev/null
+++ b/firmware/e300/rev_c/debug.h
@@ -0,0 +1,90 @@
+/*
+ * debug.h
+ */
+
+#ifndef DEBUG_H_
+#define DEBUG_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <avr/pgmspace.h>
+
+#include "io.h"
+
+#ifdef DEBUG
+#define DEBUG_INLINE
+#define DEBUG_NOOP ;
+#define LED_ON false
+#define LED_OFF true
+#else
+#define DEBUG_INLINE inline
+#define DEBUG_NOOP {}
+#define LED_ON true
+#define LED_OFF false
+#endif // DEBUG
+
+//#define DEBUG_VOID
+#define DEBUG_SAFETY
+
+#ifdef DEBUG_VOID
+
+//#define debug_init (void)
+#define debug_set (void)
+#define debug_blink (void)
+#define debug_blink_rev (void)
+#define debug_blink2 (void)
+#define debug_wait (void)
+
+#else
+
+//DEBUG_INLINE void debug_init(void) DEBUG_NOOP
+DEBUG_INLINE void debug_set(io_pin_t pin, bool enable) DEBUG_NOOP
+DEBUG_INLINE void debug_blink(uint8_t count) DEBUG_NOOP
+//DEBUG_INLINE void debug_blink_rev(uint8_t count) DEBUG_NOOP
+void debug_blink_rev(uint8_t count);
+DEBUG_INLINE void debug_blink2(uint8_t count) DEBUG_NOOP
+DEBUG_INLINE void debug_wait(void) DEBUG_NOOP
+
+#endif // DEBUG_VOID
+
+#if defined(DEBUG) && !defined(ENABLE_SERIAL)
+#define ENABLE_SERIAL
+#endif // DEBUG && !ENABLE_SERIAL
+
+/*DEBUG_INLINE */void debug_init(void)/* DEBUG_NOOP*/;
+
+#ifdef ENABLE_SERIAL
+
+void debug_log_ex_P(const char* message, bool new_line);
+void debug_log_hex_ex(uint8_t n, bool new_line);
+void debug_log_byte_ex(uint8_t n, bool new_line);
+void _debug_log_ex(const char* message, bool new_line);
+
+// Prototypes to silence avr-gcc
+inline void debug_log_P(const char* message);
+inline void debug_log_hex(uint8_t n);
+inline void debug_log_byte(uint8_t n);
+inline void _debug_log(const char* message);
+
+inline void debug_log_P(const char* message) { debug_log_ex_P(message, true); }
+inline void debug_log_hex(uint8_t n) { debug_log_hex_ex(n, true); }
+inline void debug_log_byte(uint8_t n) { debug_log_byte_ex(n, true); }
+inline void _debug_log(const char* message) { _debug_log_ex(message, true); }
+
+#else
+
+inline void debug_log_ex_P (const char* message, bool new_line) {};
+inline void debug_log_hex_ex (uint8_t n, bool new_line) {};
+inline void debug_log_byte_ex (uint8_t n, bool new_line) {};
+inline void _debug_log_ex (const char* message, bool new_line) {};
+
+#define debug_log_P (void)
+#define debug_log_hex (void)
+#define debug_log_byte (void)
+#define _debug_log (void)
+#endif // ENABLE_SERIAL
+
+#define debug_log(x) debug_log_P(PSTR(x))
+#define debug_log_ex(x,nl) debug_log_ex_P(PSTR(x), nl)
+
+#endif /* DEBUG_H_ */
diff --git a/firmware/e300/rev_c/error.h b/firmware/e300/rev_c/error.h
new file mode 100644
index 000000000..82a8f0aca
--- /dev/null
+++ b/firmware/e300/rev_c/error.h
@@ -0,0 +1,31 @@
+/*
+ * error.h
+ *
+ * Created: 4/09/2012 6:25:53 PM
+ * Author: Balint Seeber
+ */
+
+
+#ifndef ERROR_H_
+#define ERROR_H_
+
+enum ErrorBlinkCount // Lower number = higher priority
+{
+ BlinkError_None,
+ // Low power/battery
+ BlinkError_LowVoltage,
+ BlinkError_LTC3675_UnderVoltage = BlinkError_LowVoltage,
+ BlinkError_LTC4155_UnderVoltage = BlinkError_LowVoltage, // FIXME: This does not work when checking status
+ // Should match power boot steps
+ BlinkError_FPGA_Power,
+ BlinkError_DRAM_Power,
+ BlinkError_1_8V_Peripherals_Power,
+ BlinkError_3_3V_Peripherals_Power,
+ BlinkError_TX_Power,
+ // LTC3675
+ BlinkError_LTC3675_OverTemperature,
+ // LTC4155
+ BlinkError_LTC4155_BadCell
+};
+
+#endif /* ERROR_H_ */
diff --git a/firmware/e300/rev_c/global.h b/firmware/e300/rev_c/global.h
new file mode 100644
index 000000000..50fab581d
--- /dev/null
+++ b/firmware/e300/rev_c/global.h
@@ -0,0 +1,49 @@
+/*
+ * global.h
+ *
+ * Created: 31/08/2012 8:47:14 PM
+ * Author: Balint Seeber
+ */
+
+#ifndef GLOBAL_H_
+#define GLOBAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+typedef struct State
+{
+ bool interrupts_enabled;
+ uint8_t interrupt_depth;
+ //bool timers_running;
+ uint8_t active_timers;
+ bool powered;
+ bool battery_not_present;
+ bool battery_charging;
+ bool wake_up;
+ bool power_off;
+ bool core_power_bad;
+ bool ltc3675_irq;
+#ifdef CHARGER_TI
+ bool bq24190_irq;
+#else
+ bool ltc4155_irq;
+#endif // CHARGER_TI
+ //bool low_battery;
+ uint8_t blink_error;
+ uint8_t blinker_state;
+ uint8_t blink_loops;
+ uint8_t blink_last_loop;
+ bool blink_stop;
+} STATE;
+
+//extern volatile bool _timers_running;
+extern volatile STATE _state;
+
+void pmc_set_blink_error(uint8_t count);
+uint8_t pmc_get_blink_error(void);
+
+bool pmc_mask_irqs(bool mask);
+
+#endif /* GLOBAL_H_ */
diff --git a/firmware/e300/rev_c/i2c.c b/firmware/e300/rev_c/i2c.c
new file mode 100644
index 000000000..70a28e61a
--- /dev/null
+++ b/firmware/e300/rev_c/i2c.c
@@ -0,0 +1,518 @@
+#include "config.h"
+#include "i2c.h"
+
+#include <util/delay.h>
+
+#include "io.h"
+#include "debug.h"
+
+/*
+ - Reset bus on failure (lack of ACK, etc)
+ - Clock stretching
+ - In pull-up mode, much code was commented out to ever avoid driving the bus (for a fleeting moment) as this was visible on the scope as short peaks (instead the line will briefly go Hi Z).
+*/
+
+volatile bool _i2c_disable_ack_check = false;
+
+// FIXME: Follow magic numbers should be in a struct that is passed into each function
+
+#define I2C_DEFAULT_RETRY_DELAY 1 // us MAGIC
+#define I2C_DEFAULT_MAX_ACK_RETRIES 10 // * I2C_DEFAULT_RETRY_DELAY us
+
+#define I2C_DEFAULT_BUS_WAIT 10 // us MAGIC
+#define I2C_DEFAULT_MAX_BUS_RETRIES 10
+
+#define I2C_DEFAULT_SCL_LOW_PERIOD 2 // 1.3 us
+#define I2C_DEFAULT_SCL_HIGH_PERIOD 1 // 0.6 us
+#define I2C_DEFAULT_BUS_FREE_TIME 2 // 1.3 us
+#define I2C_DEFAULT_STOP_TIME 1 // 0.6 us
+
+#define I2C_DELAY _delay_us // _delay_ms
+
+static bool _i2c_start_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+ // Assumes: SDA/SCL are both inputs
+
+ uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while ((io_test_pin(sda) == false) || (io_test_pin(scl) == false))
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (retries-- == 0)
+ {
+debug_log("I2C:S1");
+ return false;
+ }
+ }
+
+ // START condition
+// if (pull_up == false)
+ io_clear_pin(sda); // Set LOW before switching to output
+ io_output_pin(sda);
+// if (pull_up)
+// io_clear_pin(sda);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); // Thd, sta
+
+ retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while (io_test_pin(scl) == false) // SCL should remain high
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (retries-- == 0)
+ {
+ io_input_pin(sda);
+debug_log_ex("I2C:S2", false);
+debug_log_hex(scl);
+ return false;
+ }
+ }
+
+// if (pull_up == false)
+ io_clear_pin(scl);
+ io_output_pin(scl);
+// if (pull_up)
+// io_clear_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD / 2); // MAGIC
+
+ return true;
+}
+
+static bool _i2c_stop_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+ // Assumes:
+ // SCL is output & LOW
+ // SDA is input (Hi-Z, or pull-up enabled)
+
+ // Assuming pull-up already enabled
+ //if (pull_up)
+ // io_set_pin(sda);
+
+ bool result = true;
+
+ // SDA should be HIGH after ACK has been clocked away
+// bool skip_drive = false;
+ uint8_t retries = 0;
+ while (io_test_pin(sda) == false)
+ {
+ if (retries == I2C_DEFAULT_MAX_ACK_RETRIES)
+ {
+ debug_log_ex("I2C:STP ", false);
+ debug_log_hex(sda);
+ debug_blink_rev(4);
+
+// skip_drive = true;
+ result = false;
+ break; // SDA is being held low?!
+ }
+
+ ++retries;
+ I2C_DELAY(I2C_DEFAULT_RETRY_DELAY);
+ }
+
+ // STOP condition
+// if ((pull_up == false) || (skip_drive))
+ io_clear_pin(sda); // Don't tri-state if internal pull-up is used
+// //else
+// // Pin will now be driven, but having checked SDA is HIGH above means slave's SDA should be Open Collector (i.e. it won't blow up)
+ io_output_pin(sda); // Drive LOW
+// if (pull_up)
+// io_clear_pin(sda);
+
+ ///////////////////////////////////
+
+// if (pull_up)
+// io_set_pin(scl); // Don't tri-state if internal pull-up is used. Line will be driven, but assuming this is the only master on the clock line (i.e. no one else will pull it low).
+ io_input_pin(scl);
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_STOP_TIME);
+
+ ///////////////////////////////////
+
+// if ((pull_up) && (skip_drive == false))
+// io_set_pin(sda); // Don't tri-state if internal pull-up is used
+ io_input_pin(sda);
+// if ((pull_up) && (skip_drive))
+ io_set_pin(sda);
+ I2C_DELAY(I2C_DEFAULT_BUS_FREE_TIME);
+
+ return result;
+}
+/*
+static void _i2c_stop(io_pin_t sda, io_pin_t scl)
+{
+ _i2c_stop_ex(sda, scl, false);
+}
+*//*
+static void _i2c_abort_safe_ex(io_pin_t pin, bool pull_up)
+{
+ if (io_is_output(pin))
+ {
+ if (io_is_pin_set(pin)) // This is bad - hope no slave is pulling down the line
+ {
+ io_input_pin(pin); // Pull-up already enabled
+
+ if (pull_up == false)
+ io_clear_pin(pin); // Doing this after changing direction ensures the line is not brought down
+ }
+ else // Currently pulling line down
+ {
+ io_input_pin(pin); // Hi-Z
+
+ if (pull_up) // There will be a moment where the line will float (better than driving the line though...)
+ {
+ io_set_pin(pin);
+ }
+ }
+ }
+ else // Already an input
+ {
+ if (pull_up)
+ {
+ io_set_pin(pin); // Enable pull-ups
+ }
+ else
+ {
+ io_clear_pin(pin); // Disable pull-ups
+ }
+ }
+
+ // Normally: pin will be Hi-Z input
+ // With internal pull-up: pin will be input with pull-up enabled
+}
+*/
+static void _i2c_abort_safe(io_pin_t pin, bool pull_up)
+{
+ if (pull_up == false)
+ io_clear_pin(pin); // Should never be output/HIGH, could be input/<was outputting HIGH> so disable pull-ups
+
+ io_input_pin(pin);
+
+ if (pull_up)
+ io_set_pin(pin); // Enable pull-up
+}
+
+static void _i2c_abort_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+/* if (pull_up == false)
+ {
+ io_clear_pin(sda);
+ io_clear_pin(scl);
+ }
+
+ io_input_pin(scl);
+ io_input_pin(sda);
+
+ if (pull_up)
+ {
+ io_set_pin(sda);
+ io_set_pin(scl);
+ }
+*/
+ _i2c_abort_safe(scl, pull_up);
+ _i2c_abort_safe(sda, pull_up);
+
+ //_i2c_abort_safe_ex(scl, pull_up);
+ //_i2c_abort_safe_ex(sda, pull_up);
+}
+/*
+static void _i2c_abort(io_pin_t sda, io_pin_t scl)
+{
+ _i2c_abort_ex(sda, scl, false);
+}
+*/
+static bool _i2c_write_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t value, bool pull_up)
+{
+ // Assumes:
+ // SDA output is LOW
+ // SCL output is LOW
+
+ for (uint8_t i = 0; i < 8; ++i)
+ {
+ bool b = ((value & (0x01 << (7 - i))) != 0x00); // MSB first
+
+ if (b)
+ {
+ if (pull_up)
+ {
+// io_set_pin(sda); // This is bad (will drive line for a moment), but more stable than letting line float
+ io_input_pin(sda);
+ io_set_pin(sda);
+ }
+ else
+ io_input_pin(sda); // Release HIGH
+
+ if (io_test_pin(sda) == false)
+ {
+ debug_log("I2C:WR ");
+ debug_log_hex(sda);
+ debug_blink_rev(1);
+ return false;
+ }
+ }
+ else
+ {
+ if (pull_up)
+ {
+// if (io_is_output(sda))
+ io_clear_pin(sda);
+// else
+// {
+ io_output_pin(sda); // [This is bad (will drive line for a moment), but more stable than letting line float]
+// io_clear_pin(sda);
+// }
+ }
+ else
+ {
+ io_enable_pin(sda, false);
+ io_output_pin(sda); // Drive LOW
+ }
+ }
+
+ ///////////////////////////////
+
+ io_input_pin(scl); // Release HIGH
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+#ifdef I2C_ALLOW_CLOCK_STRETCH
+ uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while (io_test_pin(scl) == false) // Clock stretch requested?
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (--retries == 0)
+ {
+ io_input_pin(sda); // Release HIGH
+ if (pull_up)
+ io_set_pin(sda);
+
+ debug_log_ex("I2C:STRTCH ", false);
+ debug_log_hex(scl);
+ debug_blink_rev(2);
+ return false;
+ }
+ }
+#endif // I2C_ALLOW_CLOCK_STRETCH
+ if (pull_up)
+ io_clear_pin(scl);
+ io_output_pin(scl); // Drive LOW
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);
+ }
+
+ io_input_pin(sda); // Release HIGH
+ if (pull_up)
+ io_set_pin(sda); // Assuming letting line float won't confuse slave when pulling line LOW for ACK
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+
+ uint8_t retries = 0;
+ while ((_i2c_disable_ack_check == false) && (io_test_pin(sda)))
+ {
+ if (retries == I2C_DEFAULT_MAX_ACK_RETRIES)
+ {
+ debug_log_ex("I2C:ACK ", false);
+ debug_log_hex_ex(sda, false);
+ debug_log_hex(value);
+ debug_blink_rev(3);
+ return false; // Will abort and not release bus - done by caller
+ }
+
+ ++retries;
+ I2C_DELAY(I2C_DEFAULT_RETRY_DELAY);
+ }
+
+ // Clock away acknowledge
+// if (pull_up)
+// io_set_pin(scl);
+ io_input_pin(scl); // Release HIGH
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+
+ if (pull_up)
+ io_clear_pin(scl);
+ io_output_pin(scl); // Drive LOW
+// if (pull_up)
+// io_clear_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);
+
+ return true;
+}
+
+static bool _i2c_read_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t* value, bool pull_up)
+{
+ // Assumes:
+ // SDA output is LOW
+ // SCL output is LOW
+
+ io_input_pin(sda);
+ if (pull_up)
+ io_set_pin(sda); // OK to leave line floating for a moment (better not to drive as slave will be pulling it to ground)
+
+ (*value) = 0x00;
+
+ for (uint8_t i = 0; i < 8; ++i)
+ {
+// if (pull_up)
+// io_set_pin(scl); // [Not ideal with pull-up]
+ io_input_pin(scl); // Release HIGH
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD);
+#ifdef I2C_ALLOW_CLOCK_STRETCH
+ uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES;
+ while (io_test_pin(scl) == false) // Clock stretch requested?
+ {
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+ if (--retries == 0)
+ {
+ debug_log_ex("I2C:R ");
+ debug_log_hex(scl);
+ debug_blink_rev(5);
+ return false;
+ }
+ }
+#endif // I2C_ALLOW_CLOCK_STRETCH
+ (*value) |= ((io_test_pin(sda) ? 0x1 : 0x0) << (7 - i)); // MSB first
+
+ if (pull_up)
+ io_clear_pin(scl);
+ io_output_pin(scl); // Drive LOW (not ideal with pull-up)
+// if (pull_up)
+// io_clear_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);
+ }
+
+ // Not necessary to ACK since it's only this one byte
+
+ return true;
+}
+
+bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up)
+{
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ return false;
+
+ if (_i2c_write_byte_ex(sda, scl, addr & ~0x01, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R21:", false);
+ debug_log("R21");
+ //debug_log_hex(addr);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R22:", false);
+ debug_log("R22");
+ //debug_log_hex(subaddr);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ io_input_pin(scl);
+ if (pull_up)
+ io_set_pin(scl);
+ I2C_DELAY(I2C_DEFAULT_BUS_WAIT);
+
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ {
+ return false;
+ }
+
+ if (_i2c_write_byte_ex(sda, scl, addr | 0x01, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R23:", false);
+ debug_log("R23");
+ //debug_log_hex(addr);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ //debug_log_ex("R24:", false);
+ debug_log("R24");
+ //debug_log_hex(*value);
+#endif // I2C_EXTRA_DEBUGGING
+ goto i2c_read2_fail;
+ }
+
+ if (_i2c_stop_ex(sda, scl, pull_up) == false)
+ {
+#ifdef I2C_EXTRA_DEBUGGING
+ debug_log("R25");
+#endif // I2C_EXTRA_DEBUGGING
+ }
+
+ return true;
+i2c_read2_fail:
+ _i2c_abort_ex(sda, scl, pull_up);
+ return false;
+}
+
+bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up)
+{
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ return false;
+
+ if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false)
+ goto i2c_write_fail;
+
+ if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false)
+ goto i2c_write_fail;
+
+ if (_i2c_write_byte_ex(sda, scl, value, pull_up) == false)
+ goto i2c_write_fail;
+
+ _i2c_stop_ex(sda, scl, pull_up);
+
+ return true;
+i2c_write_fail:
+ _i2c_abort_ex(sda, scl, pull_up);
+ return false;
+}
+
+bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value)
+{
+ return i2c_write_ex(sda, scl, addr, subaddr, value, false);
+}
+
+bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up)
+{
+ if (_i2c_start_ex(sda, scl, pull_up) == false)
+ return false;
+
+ if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false)
+ goto i2c_read_fail;
+
+ if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false)
+ goto i2c_read_fail;
+
+ if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false)
+ goto i2c_read_fail;
+
+ _i2c_stop_ex(sda, scl, pull_up);
+
+ return true;
+i2c_read_fail:
+ _i2c_abort_ex(sda, scl, pull_up);
+ return false;
+}
+
+bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value)
+{
+ return i2c_read_ex(sda, scl, addr, subaddr, value, false);
+}
+
+void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up)
+{
+ _i2c_abort_ex(sda, scl, pull_up);
+}
+
+void i2c_init(io_pin_t sda, io_pin_t scl)
+{
+ i2c_init_ex(sda, scl, false);
+}
diff --git a/firmware/e300/rev_c/i2c.h b/firmware/e300/rev_c/i2c.h
new file mode 100644
index 000000000..5898e7e43
--- /dev/null
+++ b/firmware/e300/rev_c/i2c.h
@@ -0,0 +1,17 @@
+#ifndef I2C_H
+#define I2C_H
+
+#include "io.h"
+
+void i2c_init(io_pin_t sda, io_pin_t scl);
+bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value);
+bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value);
+
+void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up);
+bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up);
+bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up);
+bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up);
+
+extern volatile bool _i2c_disable_ack_check;
+
+#endif // I2C_H
diff --git a/firmware/e300/rev_c/io.c b/firmware/e300/rev_c/io.c
new file mode 100644
index 000000000..0256181c6
--- /dev/null
+++ b/firmware/e300/rev_c/io.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009-2012 Ettus Research LLC
+ */
+
+#include "io.h"
+#include <avr/io.h>
+
+#define _GET_PIN(pin) ((pin) & 0xf)
+#define _GET_MASK(pin) (_BV(_GET_PIN(pin)))
+#define _GET_REG(pin, reg_x) (*reg_x[pin >> 4])
+
+#ifndef IO_DEBUG
+static volatile uint8_t *ddr_x[] = {&DDRA, &DDRB, &DDRC, &DDRD}; // 0: input, 1: output
+static volatile uint8_t *port_x[] = {&PORTA, &PORTB, &PORTC, &PORTD}; // Port contents (will appear at output if direction is set to output. If input, '1' enables pull-ups, '0' set tri-state)
+static volatile uint8_t *pin_x[] = {&PINA, &PINB, &PINC, &PIND}; // Port contents (input) If output, will return value on PORT
+#endif
+
+void io_output_pin(io_pin_t pin){
+#ifndef IO_DEBUG
+ _GET_REG(pin, ddr_x) |= _GET_MASK(pin);
+#endif
+}
+
+void io_input_pin(io_pin_t pin){
+#ifndef IO_DEBUG
+ _GET_REG(pin, ddr_x) &= ~_GET_MASK(pin);
+#endif
+}
+
+bool io_is_output(io_pin_t pin){
+#ifndef IO_DEBUG
+ return bit_is_set(_GET_REG(pin, ddr_x), _GET_PIN(pin));
+#else
+ return 0;
+#endif
+}
+
+bool io_is_input(io_pin_t pin){
+ return !io_is_output(pin);
+}
+
+void io_set_pin(io_pin_t pin){ // In input mode, will enable pull-ups
+#ifndef IO_DEBUG
+ _GET_REG(pin, port_x) |= _GET_MASK(pin);
+#endif
+}
+
+void io_clear_pin(io_pin_t pin){ // In input mode, will disable pull-ups
+#ifndef IO_DEBUG
+ _GET_REG(pin, port_x) &= ~_GET_MASK(pin);
+#endif
+}
+
+bool io_is_pin_set(io_pin_t pin){
+#ifndef IO_DEBUG
+ return bit_is_set(_GET_REG(pin, port_x), _GET_PIN(pin));
+#else
+ return 0;
+#endif
+}
+
+void io_enable_pin(io_pin_t pin, bool enable){
+ if (enable)
+ io_set_pin(pin);
+ else
+ io_clear_pin(pin);
+}
+
+bool io_test_pin(io_pin_t pin){
+#ifndef IO_DEBUG
+ return bit_is_set(_GET_REG(pin, pin_x), _GET_PIN(pin));
+#else
+ return 0;
+#endif
+}
diff --git a/firmware/e300/rev_c/io.h b/firmware/e300/rev_c/io.h
new file mode 100644
index 000000000..7eea8f0a3
--- /dev/null
+++ b/firmware/e300/rev_c/io.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2009 Ettus Research LLC
+ */
+
+#ifndef IO_H
+#define IO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define IO_PX(port, pin) ((uint8_t)(((port - 'A') << 4) + pin))
+#define IO_PA(pin) IO_PX('A', pin)
+#define IO_PB(pin) IO_PX('B', pin)
+#define IO_PC(pin) IO_PX('C', pin)
+#define IO_PD(pin) IO_PX('D', pin)
+
+typedef const uint8_t io_pin_t;
+
+void io_output_pin(io_pin_t pin);
+void io_input_pin(io_pin_t pin);
+bool io_is_output(io_pin_t pin);
+bool io_is_input(io_pin_t pin);
+
+void io_set_pin(io_pin_t pin);
+void io_clear_pin(io_pin_t pin);
+void io_enable_pin(io_pin_t pin, bool enable);
+bool io_is_pin_set(io_pin_t pin);
+
+bool io_test_pin(io_pin_t pin);
+
+#endif /* IO_H */
diff --git a/firmware/e300/rev_c/ltc3675.c b/firmware/e300/rev_c/ltc3675.c
new file mode 100644
index 000000000..0f85ec7e5
--- /dev/null
+++ b/firmware/e300/rev_c/ltc3675.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2012 Ettus Research LLC
+ */
+
+/*
+ ? STOP condition after writing address on read
+ - Default buck/boost register values are OK
+*/
+
+#include "config.h"
+#include "ltc3675.h"
+
+//#include <stdio.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+#ifndef I2C_REWORK
+#include "power.h"
+#endif // I2C_REWORK
+
+const bool _ltc3675_pull_up =
+#ifdef I2C_REWORK
+ true
+#else
+ false
+#endif // I2C_REWORK
+;
+
+volatile ltc3675_reg_helper_fn _ltc3675_reg_helper;
+
+//#define HARDWIRE_ENABLE // Use hardware enable pins instead of I2C on regulators that support it
+
+#ifdef ATTINY88_DIP
+
+#ifdef HARDWIRE_ENABLE
+static io_pin_t PWR_EN1 = IO_PC(7); // Not routed by card
+static io_pin_t PWR_EN2 = IO_PA(0); // Not available on DIP
+static io_pin_t PWR_EN3 = IO_PA(1); // Not available on DIP
+static io_pin_t PWR_EN4 = IO_PB(6); // Instead of FTDI_BCD
+static io_pin_t PWR_EN5 = IO_PB(7); // Instead of FTDI_PWREN2
+#endif // HARDWIRE_ENABLE
+
+//static io_pin_t PWR_SDA = IO_PC(4);
+//static io_pin_t PWR_SCL = IO_PC(5);
+
+#else
+
+#ifdef HARDWIRE_ENABLE
+static io_pin_t PWR_EN1 = IO_PC(1);
+//static io_pin_t PWR_EN2 = IO_PC(2); // Now used by I2C for charge controller
+//static io_pin_t PWR_EN3 = IO_PC(3); // Now used by I2C for charge controller
+static io_pin_t PWR_EN4 = IO_PA(1);
+static io_pin_t PWR_EN5 = IO_PA(2);
+#endif // HARDWIRE_ENABLE
+
+#ifdef I2C_REWORK
+static io_pin_t PWR_SDA = IO_PC(2); // Instead of EN5
+static io_pin_t PWR_SCL = IO_PA(2); // Instead of EN2
+#endif // I2C_REWORK
+
+#endif // ATTINY88_DIP
+
+static io_pin_t PWR_IRQ = IO_PD(0);
+static io_pin_t WAKEUP = IO_PD(2);
+static io_pin_t ONSWITCH_DB = IO_PD(3);
+static io_pin_t PWR_RESET = IO_PD(4);
+
+#define LTC3675_BASE_ADDRESS 0x12
+#define LTC3675_WRITE_ADDRESS (LTC3675_BASE_ADDRESS + 0)
+#define LTC3675_READ_ADDRESS (LTC3675_BASE_ADDRESS + 1)
+
+#define LTC3675_RETRY_DELAY 1 // us MAGIC
+#define LTC3675_MAX_ACK_RETRIES 10 // * LTC3675_RETRY_DELAY us
+
+#define LTC3675_SCL_LOW_PERIOD 2 // 1.3 us
+#define LTC3675_SCL_HIGH_PERIOD 1 // 0.6 us
+#define LTC3675_BUS_FREE_TIME 2 // 1.3 us
+#define LTC3675_STOP_TIME 1 // 0.6 us
+
+#define LTC3675_REGULATOR_ENABLE_DELAY 10 // 50 // ms (some arbitrary value so that the external power supply can settle)
+
+enum LTC3675Registers
+{
+ LTC3675_REG_NONE = 0x00,
+ LTC3675_REG_BUCK1 = 0x01,
+ LTC3675_REG_BUCK2 = 0x02,
+ LTC3675_REG_BUCK3 = 0x03,
+ LTC3675_REG_BUCK4 = 0x04,
+ LTC3675_REG_BOOST = 0x05,
+ LTC3675_REG_BUCK_BOOST = 0x06,
+ LTC3675_REG_LED_CONFIG = 0x07,
+ LTC3675_REG_LED_DAC = 0x08,
+ LTC3675_REG_UVOT = 0x09,
+ LTC3675_REG_RSTB = 0xA0,
+ LTC3675_REG_IRQB_MASK = 0x0B,
+ LTC3675_REG_REALTIME_STATUS = 0x0C,
+ LTC3675_REG_LATCHED_STATUS = 0x0D,
+ LTC3675_REG_CLEAR_IRQ = 0x0F
+};
+
+enum LTC3675StatusBits
+{
+ LTC3675_UnderVoltage = 1 << 7,
+ LTC3675_OverTemperature = 1 << 6,
+ LTC3675_BuckBoost_PGood = 1 << 5,
+ LTC3675_Boost_PGood = 1 << 4,
+ LTC3675_Buck4_PGood = 1 << 3,
+ LTC3675_Buck3_PGood = 1 << 2,
+ LTC3675_Buck2_PGood = 1 << 1,
+ LTC3675_Buck1_PGood = 1 << 0
+};
+
+#define LTC3675_DEFAULT_BUCK_REG_VAL 0x6F
+#define LTC3675_DEFAULT_BOOST_REG_VAL 0x0F
+#define LTC3675_DEFAULT_BUCK_BOOST_REG_VAL 0x0F
+
+#define LTC3675_ENABLE_REGISTER_BIT 0x80
+
+// Max I2C rate = 400kHz
+
+static void _ltc3675_clear_irq()
+{
+ // Two-stage clear
+ i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_CLEAR_IRQ, 0x00, _ltc3675_pull_up);
+ i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_NONE, 0x00, _ltc3675_pull_up);
+}
+
+volatile uint8_t _ltc3675_last_status = 0x00;
+
+uint8_t ltc3675_get_last_status(void)
+{
+ return _ltc3675_last_status;
+}
+
+uint8_t ltc3675_reg_status_to_error(uint8_t val)
+{
+ if (((val & LTC3675_BuckBoost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_6)))
+ return BlinkError_3_3V_Peripherals_Power;
+
+ if (((val & LTC3675_Boost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_5)))
+ return BlinkError_TX_Power;
+
+ //if (((val & LTC3675_Buck4_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_4)))
+
+ if (((val & LTC3675_Buck3_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_3)))
+ return BlinkError_1_8V_Peripherals_Power;
+
+ //if (((val & LTC3675_Buck2_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_2)))
+
+ if (((val & LTC3675_Buck1_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_1)))
+ return BlinkError_DRAM_Power;
+
+ return BlinkError_None;
+}
+
+bool ltc3675_is_power_good(uint8_t val)
+{
+ return (ltc3675_reg_status_to_error(val) == BlinkError_None);
+}
+
+uint8_t ltc3675_status_to_error(uint8_t val)
+{
+ if (val & LTC3675_UnderVoltage)
+ return BlinkError_LTC3675_UnderVoltage;
+
+ if (val & LTC3675_OverTemperature)
+ return BlinkError_LTC3675_OverTemperature;
+
+ uint8_t reg_error = ltc3675_reg_status_to_error(val);
+ if (reg_error != BlinkError_None)
+ return reg_error;
+
+ return BlinkError_None;
+}
+
+bool _ltc3675_handle_irq(void)
+{
+ uint8_t val = 0x00;
+ bool result = false;
+
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_LATCHED_STATUS, &val, _ltc3675_pull_up))
+ {
+ debug_log_ex("3675LTCH ", false);
+ debug_log_hex(val);
+ }
+
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, /*LTC3675_REG_LATCHED_STATUS*/LTC3675_REG_REALTIME_STATUS, &val, _ltc3675_pull_up)) // No point acting on latched because could have been resolved
+ {
+ //debug_log_ex("3675LTCH ", false);
+ debug_log_ex("3675RT ", false);
+ debug_log_hex(val);
+
+ _ltc3675_last_status = val;
+
+ uint8_t error = ltc3675_status_to_error(val);
+
+ /*if (val & LTC3675_UnderVoltage)
+ {
+ pmc_set_blink_error(BlinkError_LTC3675_UnderVoltage);
+ //_state.low_battery = true;
+ }*/
+
+ if (error)
+ {
+ pmc_set_blink_error(error);
+
+ /*_i2c_disable_ack_check = true;
+ uint8_t chk = 0x00;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_6) << 0;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_5) << 1;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_3) << 2;
+ chk |= (_ltc3675_reg_helper)(LTC3675_REG_1) << 3;
+ i2c_write_ex(PWR_SDA, PWR_SCL, 0xFE, 0xFF, chk, _ltc3675_pull_up);
+ _i2c_disable_ack_check = false;*/
+ }
+
+ result = true;
+ }
+
+ _ltc3675_clear_irq();
+
+ return result;
+}
+
+static bool _ltc3675_get_realtime_status(uint8_t* val)
+{
+ //cli();
+
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_REALTIME_STATUS, val, _ltc3675_pull_up) == false)
+ return false;
+
+ debug_log_ex("3675RT ", false);
+ debug_log_hex(*val);
+
+ //sei();
+
+ return true;
+}
+
+int8_t ltc3675_check_status(void)
+{
+ uint8_t val = 0x00;
+
+ pmc_mask_irqs(true);
+
+ bool result = _ltc3675_get_realtime_status(&val);
+
+ pmc_mask_irqs(false);
+
+ if (result == false)
+ return -1;
+
+ //_ltc3675_last_status = val;
+
+ /*if (val & LTC3675_UnderVoltage)
+ return BlinkError_LTC3675_UnderVoltage;
+
+ if (val & LTC3675_OverTemperature)
+ return BlinkError_LTC3675_OverTemperature;
+
+ return BlinkError_None;*/
+
+ return ltc3675_status_to_error(val);
+}
+
+bool ltc3675_handle_irq(void)
+{
+ pmc_mask_irqs(true);
+
+ /*uint8_t*/bool result = _ltc3675_handle_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+static bool _ltc3675_default_reg_helper(uint8_t address)
+{
+ uint8_t val = 0x00;
+ i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, address, &val, _ltc3675_pull_up);
+ return ((val & LTC3675_ENABLE_REGISTER_BIT) == LTC3675_ENABLE_REGISTER_BIT);
+}
+
+bool ltc3675_init(ltc3675_reg_helper_fn helper)
+{
+ if (helper)
+ _ltc3675_reg_helper = helper;
+ else
+ _ltc3675_reg_helper = _ltc3675_default_reg_helper;
+#ifdef HARDWIRE_ENABLE
+ io_output_pin(PWR_EN1);
+ io_output_pin(PWR_EN2);
+ io_output_pin(PWR_EN3);
+ io_output_pin(PWR_EN4);
+ io_output_pin(PWR_EN5);
+#endif // HARDWIRE_ENABLE
+
+ /* io_output_pin(PWR_SDA);
+ io_output_pin(PWR_SCL);
+
+ // Must remain HIGH when idle
+ io_set_pin(PWR_SDA);
+ io_set_pin(PWR_SCL);
+*/
+#ifdef I2C_REWORK
+ i2c_init_ex(PWR_SDA, PWR_SCL, _ltc3675_pull_up);
+#endif // I2C_REWORK
+ io_input_pin(PWR_IRQ);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP)
+ io_set_pin(PWR_IRQ); // Enable pull-up for Open Drain
+#endif // DEBUG
+
+ io_input_pin(WAKEUP);
+ io_set_pin(WAKEUP); // Enable pull-up for Open Drain
+
+ io_input_pin(ONSWITCH_DB);
+ io_set_pin(ONSWITCH_DB); // Enable pull-up for Open Drain
+
+ io_input_pin(PWR_RESET);
+ io_set_pin(PWR_RESET); // Enable pull-up for Open Drain
+
+ _ltc3675_clear_irq(); // Clear old interrupt - state might have changed (e.g. undervoltage might have been resolved)
+
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_IRQB_MASK, 0xFF, _ltc3675_pull_up) == false) // Any PGOOD fault will pull IRQB low
+ return false;
+
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_UVOT, 0x70, _ltc3675_pull_up) == false) // 3.4V UV
+ return false;
+
+ if (ltc3675_has_interrupt())
+ _ltc3675_handle_irq();
+
+ // Non-maskable:
+ // UV warning threshold (default): 2.7V
+ // Over temp warning threshold (default): 10 degrees below
+
+ return true;
+}
+
+bool ltc3675_is_waking_up(void)
+{
+ return io_test_pin(WAKEUP);
+}
+
+static bool _ltc3675_is_pgood(uint8_t reg)
+{
+ uint8_t val = 0x00;
+ if (_ltc3675_get_realtime_status(&val) == false)
+ return false;
+ return ((reg & val) == reg);
+}
+
+static bool _ltc3675_toggle_reg(uint8_t addr, uint8_t def_reg, bool on)
+{
+ bool result = true;
+
+ //cli();
+
+ uint8_t val = 0x00 | def_reg;
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, addr, &val, _ltc3675_pull_up) == false)
+ return false;
+
+ val &= ~LTC3675_ENABLE_REGISTER_BIT;
+
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, addr, /*def_reg*/val | (on ? LTC3675_ENABLE_REGISTER_BIT : 0x00), _ltc3675_pull_up) == false)
+ //return true;
+ result = false;
+
+ if (on)
+ {
+ _delay_ms(LTC3675_REGULATOR_ENABLE_DELAY);
+ }
+
+ //sei();
+
+ return result;
+ //return true;
+}
+
+bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on)
+{
+ //debug_blink2(reg + 1);
+ debug_log_ex("3675 ", false);
+ debug_log_byte_ex(reg, true);
+
+ // Sub-address: index of regulator
+ // Data: <default reg contents> | <enable>
+
+ bool result = false;
+
+ switch (reg)
+ {
+ case LTC3675_REG_1: // Master
+ case LTC3675_REG_2: // Slave
+#ifdef HARDWIRE_ENABLE
+ io_enable_pin(PWR_EN1, on);
+ //break;
+#else
+ //debug_blink2(reg + 1);
+ if (_ltc3675_toggle_reg(LTC3675_REG_BUCK1, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false) {
+ //debug_blink2(reg + 1);
+ return false;
+ }
+ //debug_blink2(reg + 1);
+#endif // HARDWIRE_ENABLE
+ result = (_ltc3675_is_pgood(LTC3675_Buck1_PGood) == on);
+ break;
+ case LTC3675_REG_3: // Master
+ case LTC3675_REG_4: // Slave
+#ifdef HARDWIRE_ENABLE
+ io_enable_pin(PWR_EN3, on);
+ //break;
+#else
+ if (_ltc3675_toggle_reg(LTC3675_REG_BUCK3, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false)
+ return false;
+#endif // HARDWIRE_ENABLE
+ result = (_ltc3675_is_pgood(LTC3675_Buck3_PGood) == on);
+ break;
+ case LTC3675_REG_5: // I2C only
+ if (_ltc3675_toggle_reg(LTC3675_REG_BOOST, LTC3675_DEFAULT_BOOST_REG_VAL, on) == false) // (Boost address, Default reg contents | Enable)
+ return false;
+ result = (_ltc3675_is_pgood(LTC3675_Boost_PGood) == on);
+ break;
+ case LTC3675_REG_6: // Single
+#ifdef HARDWIRE_ENABLE
+ io_enable_pin(PWR_EN5, on);
+ //break;
+#else
+ if (_ltc3675_toggle_reg(LTC3675_REG_BUCK_BOOST, LTC3675_DEFAULT_BUCK_BOOST_REG_VAL, on) == false)
+ return false;
+#endif // HARDWIRE_ENABLE
+ result = (_ltc3675_is_pgood(LTC3675_BuckBoost_PGood) == on);
+ break;
+ //default:
+ // return false;
+ }
+
+ _debug_log((result ? "+" : "-"));
+
+ return result;
+}
+
+bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage)
+{
+ // Not necessary due to R-bridges and default DAC registers
+
+ // VRAM will be 1.3579 - a little high? (re-program DAC reference)
+ // No: minimum FB step will put Vout < 1.35
+
+ uint16_t max_voltage = 0;
+ uint8_t reg_subaddr = 0;
+
+ switch (reg)
+ {
+ case LTC3675_REG_1: // 1A Buck
+ case LTC3675_REG_2: // 1A Buck
+ max_voltage = 1500;
+ reg_subaddr = LTC3675_REG_BUCK1;
+ break;
+ case LTC3675_REG_3: // 500mA Buck
+ case LTC3675_REG_4: // 500mA Buck
+ max_voltage = 1800;
+ reg_subaddr = LTC3675_REG_BUCK3;
+ break;
+ case LTC3675_REG_5: // 1A Boost
+ max_voltage = 5000;
+ reg_subaddr = LTC3675_REG_BOOST;
+ break;
+ case LTC3675_REG_6: // 1A Buck-Boost
+ max_voltage = 3300;
+ reg_subaddr = LTC3675_REG_BUCK_BOOST;
+ break;
+ }
+
+ if (voltage > max_voltage)
+ return false;
+
+ //uint32_t rMax = ((uint32_t)voltage * 1000) / (uint32_t)max_voltage;
+ //uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800;
+ uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800; // 800mV full-scale feedback voltage
+ uint32_t r = ((uint32_t)voltage * 1000) / (uint32_t)rFB;
+ if (r < 450)
+ return false;
+
+ uint16_t rDAC = (16 * ((uint16_t)r - 450)) / (800 - 450);
+
+ debug_log_ex("Vr ", false);
+ debug_log_byte_ex(reg, false);
+ debug_log_ex("=", false);
+ debug_log_byte_ex((uint8_t)rDAC, false);
+
+ uint8_t val = 0x00;
+ if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, reg_subaddr, &val, _ltc3675_pull_up) == false)
+ {
+ debug_log("-");
+ return false;
+ }
+
+ val = (val & 0xF0) | (uint8_t)rDAC;
+ if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, reg_subaddr, val, _ltc3675_pull_up) == false)
+ {
+ debug_log("-");
+ return false;
+ }
+
+ debug_log("+");
+
+ return true;
+}
+
+bool ltc3675_is_power_button_depressed(void)
+{
+ return (io_test_pin(ONSWITCH_DB) == false);
+}
+
+bool ltc3675_has_interrupt(void)
+{
+ return (io_test_pin(PWR_IRQ) == false);
+}
diff --git a/firmware/e300/rev_c/ltc3675.h b/firmware/e300/rev_c/ltc3675.h
new file mode 100644
index 000000000..d7c4baa59
--- /dev/null
+++ b/firmware/e300/rev_c/ltc3675.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 Ettus Research LLC
+ */
+
+#ifndef LTC3675_H
+#define LTC3675_H
+
+//#include "types.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef bool (*ltc3675_reg_helper_fn)(uint8_t address);
+
+bool ltc3675_init(ltc3675_reg_helper_fn helper);
+
+typedef enum ltc3675_regulators {
+ LTC3675_REG_1, // 1A Buck
+ LTC3675_REG_2, // 1A Buck
+ LTC3675_REG_3, // 500mA Buck
+ LTC3675_REG_4, // 500mA Buck
+ LTC3675_REG_5, // 1A Boost
+ LTC3675_REG_6 // 1A Buck-Boost
+ // LED Boost
+} ltc3675_regulator_t;
+
+bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on);
+bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage);
+bool ltc3675_is_power_button_depressed(void);
+bool ltc3675_has_interrupt(void);
+bool ltc3675_handle_irq(void);
+int8_t ltc3675_check_status(void);
+uint8_t ltc3675_get_last_status(void);
+uint8_t ltc3675_status_to_error(uint8_t val);
+bool ltc3675_is_power_good(uint8_t val);
+bool ltc3675_is_waking_up(void);
+
+#endif /* LTC3675_H */
diff --git a/firmware/e300/rev_c/ltc4155.c b/firmware/e300/rev_c/ltc4155.c
new file mode 100644
index 000000000..5f404e651
--- /dev/null
+++ b/firmware/e300/rev_c/ltc4155.c
@@ -0,0 +1,402 @@
+/*
+ * ltc4155.c
+ */
+
+#ifndef CHARGER_TI
+
+#include "config.h"
+#include "ltc4155.h"
+
+#include <util/delay.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "power.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+static io_pin_t USBPM_IRQ = IO_PB(1);
+
+#ifdef ATTINY88_DIP
+
+static io_pin_t CHRG_SDA = IO_PC(2);
+static io_pin_t CHRG_SCL = IO_PC(3);
+
+#else
+
+#ifdef I2C_REWORK
+
+static io_pin_t CHRG_SDA = IO_PC(4);
+static io_pin_t CHRG_SCL = IO_PC(5);
+
+#else
+
+#define CHRG_SDA PWR_SDA
+#define CHRG_SCL PWR_SCL
+
+#endif // I2C_REWORK
+
+#endif // ATTINY88_DIP
+
+const bool _ltc4155_pull_up = false;
+
+#define LTC4155_BASE_ADDRESS 0x12
+#define LTC4155_WRITE_ADDRESS (LTC4155_BASE_ADDRESS + 0)
+#define LTC4155_READ_ADDRESS (LTC4155_BASE_ADDRESS + 1)
+/*
+#define LTC4155_RETRY_DELAY 1 // us MAGIC
+#define LTC4155_MAX_ACK_RETRIES 10 // * LTC4155_RETRY_DELAY us
+
+#define LTC4155_SCL_LOW_PERIOD 2 // 1.3 us
+#define LTC4155_SCL_HIGH_PERIOD 1 // 0.6 us
+#define LTC4155_BUS_FREE_TIME 2 // 1.3 us
+#define LTC4155_STOP_TIME 1 // 0.6 us
+*/
+enum LTC4155Registers
+{
+ LTC4155_REG_USB = 0x00, // W/R
+ LTC4155_REG_WALL = 0x01, // W/R
+ LTC4155_REG_CHARGE = 0x02, // W/R
+ LTC4155_REG_STATUS = 0x03, // R
+ LTC4155_REG_GOOD = 0x04, // R
+ LTC4155_REG_THERMISTOR = 0x05, // R
+ LTC4155_REG_ENABLE = 0x06, // W/R
+ LTC4155_REG_ARM_AND_SHIP= 0x07 // W
+};
+
+enum LTC4155InterruptMasks // LTC4155_REG_ENABLE
+{
+ LTC4155_ENABLE_USB_OTG = 1 << 1,
+
+ LTC4155_INT_UVCL = 1 << 2,
+ LTC4155_INT_ILIMIT = 1 << 3,
+ LTC4155_INT_USB_OTG = 1 << 4,
+ LTC4155_INT_EXT_PWR = 1 << 5,
+ LTC4155_INT_FAULT = 1 << 6,
+ LTC4155_INT_CHARGER = 1 << 7
+};
+
+enum LTC4155Options // LTC4155_REG_USB
+{
+ LTC4155_USB_OTG_LOCKOUT = 1 << 5,
+ LTC4155_ENABLE_BATTERY_CONDITIONER = 1 << 6,
+ LTC4155_DISABLE_INPUT_UVCL = 1 << 7
+};
+
+enum LTC4155Shifts
+{
+ LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT = 4,
+ LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE = 2,
+ LTC4155_SHIFTS_WALL_PRIORITY = 7,
+ LTC4155_SHIFTS_WALL_SAFETY_TIMER = 5
+};
+
+enum LTC4155Statuses // LTC4155_REG_STATUS
+{
+ LTC4155_LOW_BATTERY = 1 << 0,
+ LTC4155_BOOST_ENABLE = 1 << 3,
+ LTC4155_ID_PIN_DETECT = 1 << 4,
+};
+
+enum LTC4155Goods // LTC4155_REG_GOOD
+{
+ LTC4155_BAD_CELL_FAULT = 1 << 0,
+ LTC4155_OTG_FAULT = 1 << 1,
+ LTC4155_OVP_ACTIVE = 1 << 2,
+ LTC4155_INPUT_UVCL_ACTIVE = 1 << 3,
+ LTC4155_INPUT_CURRENT_LIMIT_ACTIVE = 1 << 4,
+ LTC4155_WALLSNS_GOOD = 1 << 5,
+ LTC4155_USBSNS_GOOD = 1 << 6,
+ LTC4155_EXTERNAL_POWER_GOOD = 1 << 7
+};
+
+enum LTC4155BatteryChargerStatues
+{
+ LTC4155_CHARGER_OFF,
+ LTC4155_CHARGER_LOW_BATTERY_VOLTAGE,
+ LTC4155_CHARGER_CONSTANT_CURRENT,
+ LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX,
+ LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX,
+ LTC4155_CHARGER_NTC_TOO_WARM,
+ LTC4155_CHARGER_NTC_TOO_COLD,
+ LTC4155_CHARGER_NTC_HOT
+};
+
+enum LTC4155ThermistorStatuses
+{
+ LTC4155_NTC_NORMAL,
+ LTC4155_NTC_TOO_COLD,
+ LTC4155_NTC_TOO_WARM,
+ LTC4155_NTC_FAULT
+};
+
+static const uint8_t _ltc4155_interrupt_mask =
+// LTC4155_ENABLE_USB_OTG |// Enable +5V on USB connector // Is this causing the chip to power off the output?!
+ LTC4155_INT_UVCL |
+ LTC4155_INT_ILIMIT |
+ LTC4155_INT_USB_OTG |
+ LTC4155_INT_EXT_PWR | // Turn up current limit
+ LTC4155_INT_FAULT | // Blink error
+ LTC4155_INT_CHARGER; // Illuminate charge LED
+
+static bool _ltc4155_clear_irq(void)
+{
+ return i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, _ltc4155_interrupt_mask, _ltc4155_pull_up);
+}
+
+bool ltc4155_clear_irq(void)
+{
+ pmc_mask_irqs(true);
+
+ bool result = _ltc4155_clear_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+static uint8_t _ltc4155_last_good, _ltc4155_last_status;
+
+bool _ltc4155_handle_irq(void)
+{
+ _ltc4155_clear_irq(); // Clear frozen registers to get the real-time ones
+
+ _delay_ms(50); // Wait for registers to clear/update
+
+ //////////////////
+
+ uint8_t val = 0x00;
+ bool result = false;
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ //if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
+ // goto _ltc4155_handle_fail;
+
+ debug_log_ex("4155GO ", false);
+ debug_log_hex(val);
+
+ if (val & LTC4155_WALLSNS_GOOD)
+ {
+ uint8_t wall_state = 0;
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ wall_state &= ~0x1E;
+ wall_state |= 0x0E;
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ debug_log("I+");
+ }
+
+ _ltc4155_last_good = val;
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
+ goto _ltc4155_handle_fail;
+
+ //if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
+ // goto _ltc4155_handle_fail;
+
+ debug_log_ex("4155ST ", false);
+ debug_log_hex(val);
+
+ _ltc4155_last_status = val;
+
+ val >>= 5;
+
+ if (_state.blink_error == BlinkError_None)
+ {
+ switch (val)
+ {
+ case LTC4155_CHARGER_CONSTANT_CURRENT:
+ case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX:
+ case LTC4155_CHARGER_LOW_BATTERY_VOLTAGE: // If this persists for more than 1/2hr, BAD_CELL_FAULT is enabled and FAULT interrupt is generated
+ {
+ if ((_state.battery_not_present == false) &&
+ (_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD)))
+ {
+ //charge_set_led(true);
+ charge_notify(true);
+ break;
+ }
+ }
+ case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX: // Small amount of current still charging the battery but below Vc/x threshold
+ //case LTC4155_CHARGER_NTC_TOO_WARM:
+ //case LTC4155_CHARGER_NTC_TOO_COLD:
+ //case LTC4155_CHARGER_NTC_HOT:
+ // break;
+ //case LTC4155_CHARGER_OFF:
+ default:
+ //charge_set_led(false);
+ charge_notify(false);
+ }
+ }
+
+// ltc4155_dump();
+
+ result = true;
+_ltc4155_handle_fail:
+ _ltc4155_clear_irq(); // Even though it happens first above, this is necessary otherwise future IRQs won't be detected
+
+ return result;
+}
+
+#define LTC4155_CHARGE_CURRENT_LIMIT /*0xF*/0x7 // [100%] 50%
+
+bool ltc4155_set_charge_current_limit(uint8_t percentage)
+{
+ uint8_t val = 0;
+ uint8_t limit = 0;
+
+ if (percentage > 100)
+ return false;
+ else if (percentage == 100)
+ percentage = 0xF;
+ else if (percentage > 12) // 0..88 -> 0..8800
+ {
+ uint16_t l = (((uint16_t)percentage - 12) * 100) / 586;
+ limit = (uint8_t)l;
+ }
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, &val, _ltc4155_pull_up) == false)
+ return false;
+
+ val &= ((0x1 << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT) - 1);
+ //val |= (LTC4155_CHARGE_CURRENT_LIMIT << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT);
+ val |= (limit << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT);
+
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, val, _ltc4155_pull_up) == false)
+ return false;
+
+//ltc4155_dump();
+
+ return true;
+}
+
+bool ltc4155_init(bool disable_charger)
+{
+ io_input_pin(USBPM_IRQ);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP)
+ io_set_pin(USBPM_IRQ); // Enable pull-up for Open Drain
+#endif // DEBUG
+#ifdef I2C_REWORK
+ i2c_init_ex(CHRG_SDA, CHRG_SCL, _ltc4155_pull_up);
+#endif // I2C_REWORK
+ if (/*_ltc4155_clear_irq()*/_ltc4155_handle_irq() == false) // Will set interrupt masks // FIXME: Why does this cause instability?!
+ return false;
+
+ const uint8_t charge_state =
+ (disable_charger ? 0x0 : LTC4155_CHARGE_CURRENT_LIMIT) << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT | // Battery charger I limit = 100%
+ 0x3 << LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE | // FIXME: Vbatt float = 4.05V - 4.2V for LiPo (default 0x00)
+ 0x0; // Full capacity charge threshold = 10%
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, charge_state, _ltc4155_pull_up) == false)
+ return false;
+
+ const uint8_t wall_state =
+ 0x0 << LTC4155_SHIFTS_WALL_PRIORITY |
+ 0x0 << LTC4155_SHIFTS_WALL_SAFETY_TIMER | // Charge safety timer = 4hr // FIXME: 8hr or Vc/x
+ 0xE; // 3 amps, 0x1F - CLPROG1
+ if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false)
+ return false;
+
+ // FIXME:
+ // Disable ID pin detection & autonomous startup
+ // Enable OTG
+ //i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_USB, LTC4155_USB_OTG_LOCKOUT, _ltc4155_pull_up); // Disable autonomous startup
+ //i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, LTC4155_ENABLE_USB_OTG, _ltc4155_pull_up); // Enable OTG
+
+ if (_ltc4155_handle_irq() == false) // One more time (IRQ LED stays lit in dev setup)
+ return false;
+
+ return true;
+}
+
+bool ltc4155_has_interrupt(void)
+{
+ //bool state = io_test_pin(USBPM_IRQ);
+ //debug_log_ex("4155IRQ", false);
+ //debug_log_byte(state);
+ //return (state != 1);
+ return (io_test_pin(USBPM_IRQ) == false);
+}
+
+bool ltc4155_handle_irq(void)
+{
+ pmc_mask_irqs(true);
+
+ bool result = _ltc4155_handle_irq();
+
+ pmc_mask_irqs(false);
+
+ return result;
+}
+
+bool ltc4155_arm_ship_and_store(void)
+{
+ return true;
+}
+
+bool ltc4155_get_thermistor(uint8_t* val, bool* warning)
+{
+ bool result = false;
+ uint8_t _val = 0;
+
+ pmc_mask_irqs(true);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_THERMISTOR, &_val, _ltc4155_pull_up) == false)
+ goto ltc4155_get_thermistor_fail;
+
+ if (val)
+ (*val) = _val >> 1;
+
+ if (warning)
+ (*warning) = ((_val & 0x01) != 0x00);
+
+ result = true;
+ltc4155_get_thermistor_fail:
+ pmc_mask_irqs(false);
+ return result;
+}
+
+void ltc4155_dump(void)
+{
+ pmc_mask_irqs(true);
+
+ uint8_t val = 0x00;
+ bool warning = false;
+
+ if (ltc4155_get_thermistor(&val, &warning) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\tTHRM", false);
+ if (warning)
+ debug_log_ex("!", false);
+ debug_log_byte(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &val, _ltc4155_pull_up) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\tWALL", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\t4155GO ", false);
+ debug_log_hex(val);
+
+ if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false)
+ goto ltc4155_dump_fail;
+
+ debug_log_ex("\t4155ST ", false);
+ debug_log_hex(val);
+
+ltc4155_dump_fail:
+ pmc_mask_irqs(false);
+}
+
+#endif // !CHARGER_TI
diff --git a/firmware/e300/rev_c/ltc4155.h b/firmware/e300/rev_c/ltc4155.h
new file mode 100644
index 000000000..7e8e3751d
--- /dev/null
+++ b/firmware/e300/rev_c/ltc4155.h
@@ -0,0 +1,25 @@
+/*
+ * ltc4155.h
+ *
+ * Created: 17/08/2012 8:09:43 PM
+ * Author: Balint Seeber
+ */
+
+
+#ifndef LTC4155_H_
+#define LTC4155_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifndef CHARGER_TI
+
+bool ltc4155_init(bool disable_charger);
+bool ltc4155_has_interrupt(void);
+bool ltc4155_handle_irq(void);
+void ltc4155_dump(void);
+bool ltc4155_set_charge_current_limit(uint8_t percentage);
+
+#endif // !CHARGER_TI
+
+#endif /* LTC4155_H_ */
diff --git a/firmware/e300/rev_c/main.c b/firmware/e300/rev_c/main.c
new file mode 100644
index 000000000..1e065ffef
--- /dev/null
+++ b/firmware/e300/rev_c/main.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2009 Ettus Research LLC
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+
+#include "global.h"
+#include "power.h"
+#include "debug.h"
+#include "error.h"
+#include "ltc3675.h"
+#ifdef CHARGER_TI
+#include "bq24190.h"
+#else
+#include "ltc4155.h"
+#endif // CHARGER_TI
+
+#define AUTO_POWER_ON
+
+#define INITIAL_DELAY 250 // ms
+
+FUSES = { // FIXME: & FUSE_CKSEL1 for low power 128 kHz clock
+ .low = (FUSE_CKSEL0 & FUSE_SUT0 & FUSE_CKDIV8), // Internal 8MHz Oscillator, Slowly rising power (start-up time), Divide Clock by 8
+ .high = (FUSE_EESAVE & FUSE_SPIEN), // Save EEPROM between flashes // FIXME: Leave SPIEN for programming enabled?
+}; // Not using watchdog as it runs during sleep and consumes power
+
+volatile STATE _state;
+
+/*
+ - Main/shared variables must be volatile
+ - Port pins are tri-stated on reset
+ * AVR_IRQ PD(5)
+ - Enable pull-ups on all O.D. outputs from regulator chip
+ * PS_POR/SRST should be driven HIGH by ATTiny?
+ - AVR_RESET -> RESET pin - don't configure fuse (this would disable this functionality and prohibit serial programming)
+ * Ship-and-store mode for charge controller?
+ * cli before I2C calls
+ * PS_TX
+ - en5-clk, en2-data
+ * Instruction following SEI is executed before interrupts
+ * LTC3675 real-time status doesn't contain UV/OT
+ * LTC3675 PGOOD -> power down (no point in checking blink state)
+ * On WALL, use TX, on battery use OTG switcher
+ * PRR - Power Reduction Register (p40)
+ - 100% -> 50% battery charge limit
+ * Check latched status for UV/OT in 3675
+ * If blink error reset, get latest charge status from 4155
+ * Fix UV status check from 3675/4155 as they currently share the same error
+ * Use charger termination at 8hr or Vc/x
+ * Check PGood on all regs after power on before setting powered=true
+ * Re-init 4155 on soft-power on
+ - Re-set 3A limit in 4155 after external power connection
+ - Removing power when running on battery, 4155GO 0xA0 - but WALL has been removed
+ - Why is charger reporting Constant Current when power is removed
+ * ltc3675_is_power_button_depressed: check if any reg is on, otherwise value will be invalid
+ * When e.g. 3.3V doesn't come up, blink code is correctly 4 but there's a very short blink before re-starting the sequence
+ - Vprog<Vc/x
+*/
+
+bool pmc_mask_irqs(bool mask)
+{
+ if (_state.interrupts_enabled == false)
+ return false;
+
+ if (mask)
+ {
+ if (_state.interrupt_depth == 0)
+ cli();
+ ++_state.interrupt_depth;
+ }
+ else
+ {
+ if (_state.interrupt_depth == 0)
+ return false;
+
+ --_state.interrupt_depth;
+ if (_state.interrupt_depth == 0)
+ sei();
+ }
+
+ return true;
+}
+
+int main(void)
+{
+ _delay_ms(INITIAL_DELAY);
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ memset((void*)&_state, 0x00, sizeof(STATE));
+
+ debug_init();
+ debug_blink(1);
+
+ //debug_log("#"); // Will not boot if this is 21 chars long?!
+ debug_log("Hello world");
+
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN); // SLEEP_MODE_PWR_SAVE combination is documented as Reserved
+
+//ltc4155_dump();
+
+ // FIXME: Init as SPI slave (FPGA is master)
+
+ // 8-bit timer for blinking errors on charge LED
+ TCCR0A = _BV(CTC0); // CTC mode
+ OCR0A = 244; // 250ms with 1024 prescale
+ TIMSK0 = _BV(OCIE0A); // Enable CTC on Timer 0
+
+ bool init_result = power_init();
+ debug_log_ex("Init", false);
+ _debug_log(init_result ? "+" : "-");
+ debug_blink(2);
+ //debug_blink_rev(6);
+
+ ///////////////////////////////////
+#ifdef AUTO_POWER_ON
+ power_on(); // Turn on immediately. Need to de-press power button to turn off.
+ debug_log("Power");
+ debug_blink(3);
+ //debug_blink_rev(10);
+
+ //debug_wait();
+
+//ltc4155_dump();
+#endif // AUTO_POWER_ON
+ _state.interrupts_enabled = true;
+ sei(); // Enable interrupts
+
+ asm("nop");
+
+ _state.wake_up = false; // This will fire the first time the regs are turned on
+
+ bool one_more = false;
+
+ while (true)
+ {
+ one_more = false;
+#ifdef CHARGER_TI
+ if (_state.bq24190_irq)
+ {
+ bq24190_handle_irq();
+
+ _state.bq24190_irq = false;
+ }
+#else
+ if ((_state.ltc4155_irq)/* || ltc4155_has_interrupt()*/) // [Don't know why PCINT ISR misses LTC4155 IRQ on power up, so double-check state of line]
+ {
+ ltc4155_handle_irq();
+//ltc4155_dump();
+ _state.ltc4155_irq = false;
+ }
+#endif // !CHARGER_TI
+ if (_state.core_power_bad) // FIXME: Check whether it's supposed to be on
+ {
+ if (power_is_subsys_on(PS_FPGA))
+ {
+ _delay_ms(1); // Seeing weird 120us drop in PGOOD during boot from flash (no apparent drop in 1.0V though)
+
+ if (tps54478_is_power_good() == false)
+ {
+ debug_log("ML:FPGA!");
+
+ //power_off();
+ _state.power_off = true;
+
+ /*while (_state.wake_up == false)
+ {
+ blink_error_sequence(1);
+ }*/
+ pmc_set_blink_error(BlinkError_FPGA_Power); // [If a blink error was set in power_off, this will supercede it]
+ }
+ }
+
+ _state.core_power_bad = false;
+ }
+
+ if ((_state.ltc3675_irq)/* || ltc3675_has_interrupt()*/) // This is fired on initial power up
+ {
+ debug_log("ML:3675+");
+
+ ltc3675_handle_irq();
+
+ if (ltc3675_is_power_good(ltc3675_get_last_status()) == false)
+ {
+ debug_log("ML:3675!");
+
+ //power_off();
+ _state.power_off = true;
+ }
+
+ _state.ltc3675_irq = false;
+ }
+
+ if (_state.power_off)
+ {
+ debug_log("ML:Off..");
+
+ power_off();
+
+ _state.power_off = false;
+ _state.wake_up = false;
+ }
+ else if (_state.wake_up)
+ {
+ _delay_ms(1); // Tapping 3.1 ohm load ing 4155 in dev setup causes transient on this line and causes power on sequence to begin again
+
+ //if (_state.powered == false) // Don't check in case button is held long enough to force LTC3675 shutdown (will not change 'powered' value)
+ if (ltc3675_is_waking_up())
+ {
+ debug_log("ML:On..");
+
+ power_on();
+ }
+
+ _state.wake_up = false;
+ }
+
+ // Check to see if the error state has resolved itself at the end of each sequence of the current blink error
+
+ if ((_state.blink_error != BlinkError_None) && (_state.blink_last_loop != _state.blink_loops))
+ {
+ // [Check IRQs periodically]
+
+ bool ltc3675_use_last_status = false;
+ /*if (ltc3675_has_interrupt())
+ {
+ //debug_set(IO_PB(6), ((_state.blink_loops % 2) == 0));
+ ltc3675_use_last_status = true;
+ ltc3675_handle_irq();
+ }*/
+
+ ///////////////////////////
+
+ switch (_state.blink_error)
+ {
+ case BlinkError_LTC3675_UnderVoltage:
+ case BlinkError_LTC3675_OverTemperature:
+ case BlinkError_DRAM_Power:
+ case BlinkError_3_3V_Peripherals_Power:
+ case BlinkError_1_8V_Peripherals_Power:
+ case BlinkError_TX_Power:
+ if (((ltc3675_use_last_status) && (ltc3675_status_to_error(ltc3675_get_last_status()) != BlinkError_None)) ||
+ ((ltc3675_use_last_status == false) && (ltc3675_check_status() != BlinkError_None)))
+ break;
+ debug_log("BE:3675-");
+ goto cancel_blink_error;
+ case BlinkError_FPGA_Power:
+ if (tps54478_is_power_good() == false)
+ break;
+ debug_log("BE:FPGA-");
+ goto cancel_blink_error;
+ default:
+cancel_blink_error:
+ //debug_set(IO_PB(7), true);
+ pmc_set_blink_error(BlinkError_None);
+ }
+
+ ////////////////////////////////////
+
+ // More periodic checks
+ // Need to do this has some interrupts are on PCINT, and while GIE is disabled, might change & change back
+ // E.g. LTC3675 IRQ due to UV, reset IRQ, re-asserts UV
+#ifndef CHARGER_TI
+ if (ltc4155_has_interrupt())
+ {
+ debug_log("BE:4155");
+
+ _state.ltc4155_irq = true;
+ one_more = true;
+ }
+#endif // !CHARGER_TI
+ if (ltc3675_has_interrupt())
+ {
+ debug_log("BE:3675");
+
+ _state.ltc3675_irq = true;
+ one_more = true;
+ }
+
+ if (power_is_subsys_on(PS_FPGA))
+ {
+ if (tps54478_is_power_good() == false)
+ {
+ debug_log("BE:FPGA!");
+
+ _state.core_power_bad = true;
+ one_more = true;
+ }
+ }
+
+ ////////////////////////////////////
+
+ _state.blink_last_loop = _state.blink_loops;
+ }
+
+ //if (_state.timers_running == false)
+ if ((_state.active_timers == 0) && (one_more == false))
+ {
+ debug_log("^");
+ sleep_mode();
+ debug_log("$");
+ }
+ }
+
+ return 0;
+}
+
+uint8_t pmc_get_blink_error(void)
+{
+ return _state.blink_error;
+}
+
+void pmc_set_blink_error(uint8_t count)
+{
+ if ((_state.blink_error != BlinkError_None) && (count /*> _state.blink_error*/!= BlinkError_None)) // [Prioritise] Always keep first sequence running
+ return;
+ else if (_state.blink_error == count) // Don't restart if the same
+ return;
+
+ if (count == BlinkError_None)
+ {
+ debug_log("BLNK-");
+ _state.blink_stop = true;
+ return;
+ }
+
+ //char msg[25];
+ //sprintf(msg, "Blink code = %i\n", count);
+ //debug_log(msg);
+ debug_log_ex("BLNK ", false);
+ debug_log_byte(count);
+
+ _state.blink_error = count;
+ _state.blink_loops = 0;
+ _state.blink_last_loop = 0;
+ _state.blinker_state = 0;
+ _state.blink_stop = false;
+
+ charge_set_led(false);
+
+ TCNT0 = 0;
+ if ((TCCR0A & 0x07) == 0x00) // Might already be active with existing error
+ _state.active_timers++;
+ TCCR0A |= 0x05; // Start with 1024 prescale
+}
+
+ISR(TIMER0_COMPA_vect) // Blink the sequence, and leave one slot at the beginning and end where the LED is off so one can get a sense of how many blinks occurred
+{
+ pmc_mask_irqs(true);
+
+ if (_state.blinker_state < (2 * _state.blink_error + 1))
+ charge_set_led((_state.blinker_state % 2) == 1);
+
+ _state.blinker_state++;
+
+ if (_state.blinker_state == (2 * _state.blink_error + 1 + 1))
+ {
+ _state.blinker_state = 0;
+
+ if (_state.blink_stop)
+ {
+ if ((TCCR0A & 0x07) != 0x00)
+ _state.active_timers--;
+ TCCR0A &= ~0x07;
+
+ _state.blink_error = BlinkError_None;
+
+ debug_log("BLNK.");
+ }
+ else
+ {
+ _state.blink_loops++;
+ }
+ }
+
+ pmc_mask_irqs(false);
+}
diff --git a/firmware/e300/rev_c/power.c b/firmware/e300/rev_c/power.c
new file mode 100644
index 000000000..36bc81456
--- /dev/null
+++ b/firmware/e300/rev_c/power.c
@@ -0,0 +1,900 @@
+/*
+ * Test battery voltage code
+ * Charger error blinking uses busy wait - will drain battery if encounters error while unattended? Surely rest of H/W will pull more current.
+*/
+#include "config.h"
+#include "power.h"
+
+#include <string.h>
+#include <util/delay.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#include "io.h"
+#include "i2c.h"
+#include "ltc3675.h"
+#include "ltc4155.h"
+#include "bq24190.h"
+#include "debug.h"
+#include "global.h"
+#include "error.h"
+
+#define BLINK_ERROR_DELAY 250 // ms
+
+#define POWER_DEFAULT_DELAY 50 // ms
+#define POWER_DEFAULT_RETRIES 10
+
+#define BATT_MIN_VOLTAGE 2000 // mV
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define ZERO_MEMORY(s) memset(&s, 0x00, sizeof(s))
+
+#ifndef I2C_REWORK
+io_pin_t PWR_SDA = IO_PC(4);
+io_pin_t PWR_SCL = IO_PC(5);
+
+io_pin_t USB_RESETn= IO_PA(2);
+#endif // I2C_REWORK
+
+//volatile bool powered = false;
+
+#ifdef DDR3L
+#define DRAM_VOLTAGE 1350 // TODO: Misleading, actual DRAM voltage is 1.5V. This sets the regular voltage.
+#else
+#define DRAM_VOLTAGE 0 // Hardware default
+#endif // DDR3
+
+struct reg_config {
+ int16_t voltage; // mV
+ uint8_t device;
+ uint8_t address; // Device specific
+ bool powered;
+} default_reg_config[] = { // Index maps to 'power_subsystem_t', 0 volts means leave at hardware default
+ { 0000, REG_UNKNOWN, 0/*, true*/ }, // PS_UNKNOWN
+ { 1000, REG_TPS54478, 0/*, true*/ }, // PS_FPGA
+ { DRAM_VOLTAGE, REG_LTC3675, LTC3675_REG_1 }, // PS_VDRAM
+ { /*1800*/0, REG_LTC3675, LTC3675_REG_3 }, // PS_PERIPHERALS_1_8
+ { /*3300*/0, REG_LTC3675, LTC3675_REG_6 }, // PS_PERIPHERALS_3_3
+ { /*5000*/0, REG_LTC3675, LTC3675_REG_5 } // PS_TX
+};
+/*
+int8_t power_get_regulator_index(uint8_t device, uint8_t address)
+{
+ for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i)
+ {
+ struct reg_config* reg = default_reg_config + i;
+ if ((reg->device == device) && (reg->address == address))
+ return i;
+ }
+
+ return -1;
+}
+*/
+bool power_is_subsys_on(power_subsystem_t index)
+{
+ if ((index <= PS_UNKNOWN) || (index >= PS_MAX))
+ return false;
+
+ return default_reg_config[index].powered;
+}
+
+static bool ltc3675_reg_helper(uint8_t address)
+{
+ for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i)
+ {
+ struct reg_config* reg = default_reg_config + i;
+ if ((reg->device == REG_LTC3675) && (reg->address == address))
+ return reg->powered;
+ }
+#ifdef DEBUG_SAFETY
+ debug_log_ex("!3675HLP ", false);
+ debug_log_hex(address);
+#endif // DEBUG_SAFETY
+ return false;
+ //return power_is_subsys_on(power_get_regulator_index(REG_LTC3675, address) - 1);
+}
+
+static io_pin_t AVR_CS = IO_PB(2);
+static io_pin_t AVR_MOSI = IO_PB(3);
+static io_pin_t AVR_MISO = IO_PB(4);
+static io_pin_t AVR_SCK = IO_PB(5);
+
+static io_pin_t FTDI_RESETn = IO_PB(6);
+static io_pin_t FTDI_CBUS3 = IO_PB(7);
+static io_pin_t USB_CLK_EN = IO_PA(1);
+
+static io_pin_t AVR_RESET = IO_PC(6);
+static io_pin_t AVR_IRQ = IO_PD(5);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define TPS54478_START_DELAY 10 // 50 (safety) // 3 (per spec) // ms (some arbitrary value so that the external power supply can settle)
+
+#ifdef ATTINY88_DIP
+static io_pin_t CORE_PWR_EN = IO_PC(1); // IO_PC(7) not routed by card, using PWER_EN1 instead
+#else
+static io_pin_t CORE_PWR_EN = IO_PA(3);
+#endif // ATTINY88_DIP
+static io_pin_t CORE_PGOOD = IO_PB(0);
+
+void tps54478_init(bool enable)
+{
+ tps54478_set_power(enable);
+ io_clear_pin(CORE_PWR_EN);
+
+ io_input_pin(CORE_PGOOD);
+#if !defined(DEBUG) && !defined(ATTINY88_DIP) // Don't enable pull-up when connected to a pulled-up switch
+ io_set_pin(CORE_PGOOD); // Enable pull-up for Open Drain
+#endif // DEBUG
+//#ifdef DEBUG
+// io_enable_pin(CORE_PWR_EN, false);
+//#endif // DEBUG
+//_delay_ms(2500);
+}
+
+void tps54478_set_power(bool on)
+{
+ debug_log_ex("54478", false);
+
+ // Assumes: Hi-Z input/LOW output
+
+ if (on)
+ {
+ io_input_pin(CORE_PWR_EN);
+ _delay_ms(TPS54478_START_DELAY);
+
+ debug_log("+");
+ }
+ else
+ {
+ io_output_pin(CORE_PWR_EN);
+ // Don't delay here as we can't detect its state anyway
+
+ debug_log("-");
+ }
+
+ //io_enable_pin(CORE_PWR_EN, on);
+}
+
+bool tps54478_is_power_good(void)
+{
+ return io_test_pin(CORE_PGOOD); // This doesn't necessarily mean it's good - the chip might be malfunctioning (or switched off)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static io_pin_t CHARGE = IO_PD(1);
+
+#if !defined(ATTINY88_DIP) && defined(LED_POLARITY)
+static io_pin_t POWER_LED = IO_PC(7);
+
+void power_set_led_ex(bool on, bool swap)
+{
+ if (swap)
+ {
+ if ((on == false) && (/*io_is_pin_set(CHARGE)*/_state.battery_charging)) // If charging and turning off, don't change charge light
+ {
+ charge_set_led(true); // Force it again just in case
+ return;
+ }
+ }
+
+ io_clear_pin(CHARGE);
+ io_enable_pin(POWER_LED, on);
+}
+
+void power_set_led(bool on)
+{
+ power_set_led_ex(on, true);
+}
+#endif // !ATTINY88_DIP && LED_POLARITY
+
+void charge_set_led_ex(bool on, bool swap)
+{
+#ifdef ATTINY88_DIP
+ //
+#else
+
+#ifdef LED_POLARITY
+ io_clear_pin(POWER_LED);
+#endif // LED_POLARITY
+
+#endif // ATTINY88_DIP
+
+#ifdef ATTINY88_DIP
+ io_enable_pin(CHARGE, !on);
+#else
+ io_enable_pin(CHARGE, on);
+
+#ifdef LED_POLARITY
+ if (swap)
+ {
+ if ((on == false) && (_state.powered)) // If no longer charging, turn power light back on
+ power_set_led(true);
+ }
+#endif // LED_POLARITY
+
+#endif // ATTINY88_DIP
+}
+
+void charge_set_led(bool on)
+{
+ charge_set_led_ex(on, true);
+}
+
+void charge_notify(bool charging)
+{
+ _state.battery_charging = charging;
+
+ charge_set_led(charging);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void power_signal_interrupt(void)
+{
+ io_set_pin(AVR_IRQ); // FIXME: Active low?
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if !defined(DEBUG) && !(defined(ENABLE_SERIAL) && defined(ATTINY88_DIP))
+static io_pin_t PS_POR = IO_PD(6);
+#define PS_POR_AVAILABLE
+#endif // DEBUG
+static io_pin_t PS_SRST = IO_PD(7);
+
+#define FPGA_RESET_DELAY 10 // ms // MAGIC
+
+void fpga_reset(bool delay)
+{
+#ifdef PS_POR_AVAILABLE
+ io_clear_pin(PS_POR);
+#endif // PS_POR_AVAILABLE
+ io_clear_pin(PS_SRST);
+
+ if (delay)
+ _delay_ms(FPGA_RESET_DELAY);
+#ifdef PS_POR_AVAILABLE
+ io_enable_pin(PS_POR, true);
+#endif // PS_POR_AVAILABLE
+ io_enable_pin(PS_SRST, true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static io_pin_t VBAT = IO_PC(0);
+
+void battery_init(void)
+{
+ //io_input_pin(VBAT);
+ DIDR0 |= 0x1; // Digital input disable PC0 (ADC0)
+
+ ADMUX = (1 << REFS0) // AVcc reference
+ | (0 << ADLAR) // Left-aligned result
+ | (0 << MUX0); // ADC0
+
+ ADCSRA = (0x7 << ADPS0);// Prescale clock by 128
+}
+
+uint16_t battery_get_voltage(void)
+{
+ // Vout = (357k / (274k + 357k)) * Vbat
+ // Vbat = (Vout * (274k + 357k)) / 357k
+
+ // ADC = (Vin * 1024) / Vref
+ // Vin = (ADC * Vref) / 1024
+ // Vref = 3.3
+
+ // Vbat(mV) = 1000 * (((ADC * 3.3) / 1024) * (274k + 357k)) / 357k
+ // Vbat(mV) ~= ADC * 5.70
+
+ ADCSRA |= (1 << ADEN); // FIXME: Turn on ADC (or leave on all the time?)
+
+ ADCSRA |= (1 << ADSC); // Start conversion
+
+ while (ADCSRA & (1 << ADSC)); // Wait for End of Conversion
+
+ /*uint16_t*/uint32_t voltage = (ADCH << 8) | (ADCL << 0);
+#ifdef ATTINY88_DIP
+ voltage = (voltage * 32227) / 10000; // ~3.22265625
+#else
+ voltage = (voltage * 56961) / 10000; // ~5.69606748
+#endif // ATTINY88_DIP
+ ADCSRA &= ~(1 << ADEN); // FIXME: Turn off ADC (or leave on all the time?)
+
+ return (uint16_t)voltage;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void blink_error_sequence(uint8_t len)
+{
+ charge_set_led(false);
+ _delay_ms(BLINK_ERROR_DELAY * 2);
+
+ for (; len > 0; len--) {
+ charge_set_led(true);
+ _delay_ms(BLINK_ERROR_DELAY);
+ charge_set_led(false);
+ _delay_ms(BLINK_ERROR_DELAY);
+ }
+
+ //for (len = 2; len > 0; len--) // Could have *2 on delay, but so as never to overflow 8-bit argument
+ // _delay_ms(BLINK_ERROR_DELAY);
+}
+
+typedef struct power_params {
+ power_subsystem_t subsys;
+ bool enable;
+ uint8_t retry;
+ //uint16_t opaque;
+} power_params_t;
+
+static bool _power_up_fpga(power_params_t* params)
+{
+ if (params->subsys != PS_FPGA)
+ return false;
+
+ if (params->enable == false)
+ {
+ //if (tps54478_is_power_good() == false) // Already off
+ // return true;
+
+ if (params->retry == 0)
+ {
+ io_clear_pin(PS_SRST); // FIXME: Hold it low to stop
+#ifdef PS_POR_AVAILABLE
+ io_clear_pin(PS_POR); // Prepare it for shutdown, and then the potential next power cycle
+#endif // PS_POR_AVAILABLE
+ tps54478_set_power(false);
+ }
+
+ //return (tps54478_is_power_good() == false);
+ return true;
+ }
+
+ //bool fpga_power_good = tps54478_is_power_good(); // TODO: Can it ever already be good?
+
+ if (params->retry == 0)
+ tps54478_set_power(true);
+
+ return tps54478_is_power_good();
+}
+
+static bool _power_up_reg(power_params_t* params)
+{
+ if ((params->subsys > PS_TX) || (params->subsys < PS_VDRAM))
+ return false;
+
+ struct reg_config* cfg = default_reg_config + params->subsys;
+
+ if (params->enable == false)
+ return ltc3675_enable_reg(cfg->address, false);
+
+ if (cfg->voltage > 0)
+ {
+ if (ltc3675_set_voltage(cfg->address, cfg->voltage) == false)
+ return false;
+ }
+
+ return ltc3675_enable_reg(cfg->address, true);
+}
+
+static bool _power_enable_subsys(power_params_t* params)
+{
+ switch (params->subsys)
+ {
+ case PS_FPGA:
+ return _power_up_fpga(params);
+ //case PS_:
+ // break;
+ default:
+ return _power_up_reg(params);
+ }
+
+ return false; // Should never get here
+}
+
+bool power_enable(power_subsystem_t subsys, bool on)
+{
+ power_params_t params;
+ ZERO_MEMORY(params);
+ params.subsys = subsys;
+ params.enable = on;
+
+ return _power_enable_subsys(&params);
+}
+
+typedef bool (*boot_function_t)(power_params_t*);
+
+struct boot_step {
+ power_subsystem_t subsys;
+ //boot_function_t fn;
+ //uint8_t delay;
+ //uint8_t retries;
+ //uint16_t opaque;
+ //bool powered;
+} boot_steps[] = { // MAGIC: Retries/delays
+ { PS_FPGA, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 7..8 // 3..4
+ { PS_VDRAM, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 9..10 // 5..6
+ { PS_PERIPHERALS_1_8, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 11..12 // 7..8
+ { PS_PERIPHERALS_3_3, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }, // 13..14 // 9..10
+ { PS_TX, /*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ } // CHECK: Leaving TX off
+};
+/*
+bool power_is_subsys_on(int8_t index)
+{
+ if ((index < 0) || (index >= ARRAY_SIZE(boot_steps)))
+ return false;
+
+ struct boot_step* step = boot_steps + index;
+
+ return step->powered;
+}
+*/
+bool power_init(void)
+{
+ io_output_pin(CHARGE);
+#ifdef LED_POLARITY
+ io_output_pin(POWER_LED);
+#endif // LED_POLARITY
+
+ charge_set_led(true);
+
+ battery_init();
+
+ tps54478_init(true); // Will keep EN float (keep power on)
+#ifndef I2C_REWORK
+ i2c_init(PWR_SDA, PWR_SCL);
+
+
+
+#endif // I2C_REWORK
+ io_input_pin(USB_RESETn);
+ io_output_pin(FTDI_RESETn);
+ io_output_pin(USB_CLK_EN);
+ io_input_pin(FTDI_CBUS3);
+
+#ifdef CHARGER_TI
+ if (bq24190_init(true) == false)
+ return false;
+#else
+ if (ltc4155_init(/*_state.battery_not_present*/true/*false*/) == false)
+ return false;
+#endif // CHARGER_TI
+#ifdef CHARGER_TI
+ _delay_ms(1000); // Still at 1.4V on dev board
+#else
+ _delay_ms(25); // Wait for charge current to stop (Vbatt to fall to 0V)
+#endif // CHARGER_TI
+ uint16_t batt_voltage = battery_get_voltage();
+ debug_log_ex("Vb ", false);
+ debug_log_byte((uint8_t)(batt_voltage / 100));
+ //debug_log_hex_ex(batt_voltage >> 8, false);
+ //debug_log_hex(batt_voltage & 0xFF);
+ if (batt_voltage < BATT_MIN_VOLTAGE)
+ {
+ _state.battery_not_present = true;
+
+ //debug_log("NoBatt");
+ }
+ else
+ {
+#ifdef CHARGER_TI
+ bq24190_toggle_charger(true);
+#else
+ ltc4155_set_charge_current_limit(50);
+#endif // CHARGER_TI
+ }
+
+ if (ltc3675_init(ltc3675_reg_helper) == false)
+ return false;
+#ifdef PS_POR_AVAILABLE
+ io_output_pin(PS_POR);
+#endif // PS_POR_AVAILABLE
+ io_output_pin(PS_SRST);
+ // Hold low until power is stable
+#ifdef PS_POR_AVAILABLE
+ io_clear_pin(PS_POR);
+#endif // PS_POR_AVAILABLE
+ io_clear_pin(PS_SRST);
+/*
+ AVR_CS
+ AVR_MOSI
+ AVR_MISO
+ AVR_SCK
+
+ FTDI_BCD
+ FTDI_PWREN2
+*/
+ io_input_pin(AVR_RESET); // Has external pull-up (won't do anything because this is configured at the hardware RESET pin)
+
+ //io_output_pin(AVR_IRQ); // Output here, input to FPGA
+ io_input_pin(AVR_IRQ);
+ //io_set_pin(AVR_IRQ); // FIXME: Active low?
+
+ ///////////////
+
+ EICRA = _BV(ISC01) | _BV(ISC00) | _BV(ISC10)/* | _BV(ISC11)*/; // Rising edge for INT0 (WAKEUP). [Falling for INT1.] Any logical change INT1 (ONSWITCH_DB)
+ //EIMSK = _BV(INT0); // [Turn on WAKEUP interrupt] Don't do this, as unit will turn on anyway
+ EIMSK = _BV(INT1) | _BV(INT0); // Turn on ONSWITCH_DB and WAKEUP
+
+ PCMSK0 = _BV(PCINT1) | _BV(PCINT0); // USBPM_IRQ | CORE_PGOOD
+ PCMSK2 = _BV(PCINT16)/* | _BV(PCINT20)*/; // PWR_IRQ/* | PWR_RESET*/
+ PCICR = _BV(PCIE2) | _BV(PCIE0);
+
+ ///////////////
+/*
+ TCNT0;
+ OCR0A = 0x;
+ TCCR0A = _BV(CTC0);
+ TIFR0;
+ TIMSK0;
+ TCCR0A |= 0x05; // Switch on with 1024 prescaler
+*/
+ TCCR1B = _BV(WGM12); // CTC mode
+ OCR1A = 15624 * 2; // Hold button for 2 seconds to switch off
+ TIMSK1 = _BV(OCIE1A); // Enable CTC on Timer 1
+
+ charge_set_led(false);
+
+ return true;
+}
+
+bool power_on(void)
+{
+ pmc_mask_irqs(true);
+
+ //charge_set_led(false);
+
+ bool last_power_led_state = /*true*/false;
+
+ //if ((ARRAY_SIZE(boot_steps) % 2) == 0) // Should end with 'true'
+ // last_power_led_state = false;
+
+ power_set_led(last_power_led_state);
+
+ fpga_reset(true);
+
+ uint8_t step_count, retry;
+ for (step_count = 0; step_count < ARRAY_SIZE(boot_steps); step_count++)
+ {
+ last_power_led_state = !last_power_led_state;
+ power_set_led(last_power_led_state);
+
+// debug_blink(step_count);
+// debug_blink(3 + (step_count * 2) + 0);
+// debug_blink_rev(7 + (step_count * 2) + 0);
+
+ struct boot_step* step = boot_steps + step_count;
+ if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN))
+ continue;
+
+ debug_log_ex("PWR ", false);
+ debug_log_byte_ex(step->subsys, true);
+
+ power_params_t params;
+
+ for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++)
+ {
+ ZERO_MEMORY(params);
+ params.subsys = step->subsys;
+ params.enable = true;
+ params.retry = retry;
+
+ if ((/*(step->fn != NULL) && (step->fn(&params))) ||
+ ((step->fn == NULL) && */(_power_enable_subsys(&params))))
+ {
+ //step->powered = true;
+ default_reg_config[step->subsys].powered = true;
+
+ debug_log("+");
+// debug_blink(3 + (step_count * 2) + 1);
+// debug_blink_rev(7 + (step_count * 2) + 1);
+
+//ltc4155_dump();
+
+ break;
+ }
+
+ debug_log("?");
+
+ if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/)
+ _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY);
+ }
+
+// debug_blink(step_count);
+
+ if (retry == /*step->retries*/POWER_DEFAULT_RETRIES)
+ break;
+ }
+
+ if (step_count != ARRAY_SIZE(boot_steps))
+ {
+ debug_log("x");
+
+ //sei(); // For button press detection
+
+ /*while (_state.powered == false) {
+ blink_error_sequence(step_count + BlinkError_FPGA_Power);
+ }*/
+ pmc_set_blink_error(step_count + BlinkError_FPGA_Power);
+
+ pmc_mask_irqs(false);
+
+ return false;
+ }
+
+ ///////////////////////////////////
+
+ io_set_pin(USB_CLK_EN);
+ _delay_ms(200);
+ io_set_pin(FTDI_RESETn);
+ fpga_reset(false); // Power has been brought up, so let FPGA run
+ _delay_ms(100);
+
+ ///////////////////////////////////
+
+// ltc4155_dump();
+
+ // Turn off WAKEUP interrupt, enable ONSWITCH_DB
+ //EIMSK = _BV(INT1);
+
+ _state.powered = true;
+//debug_blink_rev(1);
+//_delay_ms(1000); // Wait for FPGA PGOOD to stabilise
+ pmc_mask_irqs(false);
+
+ power_set_led(true);
+
+ if (_state.battery_charging)
+ {
+ _delay_ms(500*2);
+ charge_set_led(true);
+ }
+
+ return true;
+}
+
+uint8_t power_off(void)
+{
+ pmc_mask_irqs(true);
+
+ io_clear_pin(PS_SRST); // FIXME: Hold it low to stop FPGA running
+
+ fpga_reset(true);
+ io_clear_pin(USB_CLK_EN);
+
+ bool last_power_led_state = /*false*/true;
+
+ //if ((ARRAY_SIZE(boot_steps) % 2) == 0) // Should end with 'false'
+ // last_power_led_state = true;
+
+ //power_set_led(last_power_led_state);
+
+ ///////////////////////////////////
+
+ int8_t step_count, retry;
+ for (step_count = ARRAY_SIZE(boot_steps) - 1; step_count >= 0; step_count--)
+ {
+ last_power_led_state = !last_power_led_state;
+ power_set_led(last_power_led_state);
+
+// debug_blink(step_count);
+
+ struct boot_step* step = boot_steps + step_count;
+ if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN))
+ continue;
+
+ power_params_t params;
+
+ for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++)
+ {
+ ZERO_MEMORY(params);
+ params.subsys = step->subsys;
+ params.enable = false;
+ params.retry = retry;
+
+ if ((/*(step->fn != NULL) && (step->fn(&params))) ||
+ ((step->fn == NULL) && */(_power_enable_subsys(&params))))
+ {
+ //step->powered = false;
+ default_reg_config[step->subsys].powered = false;
+ break;
+ }
+
+ if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/)
+ _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY);
+ }
+
+// debug_blink(step_count);
+
+ if (retry == /*step->retries*/POWER_DEFAULT_RETRIES)
+ break;
+ }
+
+ if (step_count != -1)
+ {
+ /*pmc_mask_irqs(false);
+
+ while (_state.powered) {
+ blink_error_sequence(step_count + BlinkError_FPGA_Power);
+ }*/
+ if (pmc_get_blink_error() == BlinkError_None) // Only set blink error if no existing error
+ pmc_set_blink_error(step_count + BlinkError_FPGA_Power);
+
+ pmc_mask_irqs(false);
+
+ return (step_count + 1);
+ }
+
+ ///////////////////////////////////
+
+ // Turn off WAKEUP interrupt, enable ONSWITCH_DB
+ //EIMSK = _BV(INT1);
+
+ _state.powered = false;
+
+ pmc_mask_irqs(false);
+
+ power_set_led_ex(false, false);
+ _delay_ms(500*2);
+
+ power_set_led(false); // Will turn on charger LED if battery is charging
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef DEBUG
+
+#ifdef ATTINY88_DIP
+static io_pin_t DEBUG_1 = IO_PB(6);
+static io_pin_t DEBUG_2 = IO_PB(7);
+#endif // ATTINY88_DIP
+
+#endif // DEBUG
+
+ISR(INT0_vect) // PD(2) WAKEUP: Rising edge
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ //power_on();
+ debug_log("\nINT0\n");
+ _state.wake_up = true;
+
+ //sei();
+ pmc_mask_irqs(false);
+}
+
+ISR(INT1_vect) // PD(3) ONSWITCH_DB (PB_STAT): Any change
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ if (ltc3675_is_power_button_depressed())
+ {
+ debug_log("PWRBTN+");
+
+ TCNT1 = 0;
+ if ((TCCR1B & 0x07) == 0x00)
+ {
+ _state.active_timers++;
+ debug_log("TIMER1+");
+ }
+ TCCR1B |= /*0x5*/0x3; // [1024] 64 prescaler
+ //_state.timers_running = true;
+
+ //debug_set(DEBUG_1, true);
+ //debug_set(DEBUG_2, false);
+ }
+ else
+ {
+ debug_log("PWRBTN-");
+
+ //if (TIMSK1 & _BV(OCIE1A)) // If letting go of button and still running, stop timer
+ {
+ //TIMSK1 &= ~_BV(OCIE1A);
+ if ((TCCR1B & 0x07) != 0x00)
+ {
+ _state.active_timers--;
+ debug_log("TIMER1-");
+ }
+ TCCR1B &= ~0x7; // Disable timer
+ //_state.timers_running = false;
+
+ //debug_set(DEBUG_1, false);
+ }
+ }
+
+ //sei();
+ pmc_mask_irqs(false);
+}
+
+ISR(TIMER1_COMPA_vect)
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ debug_log("TIMER1");
+
+ //TIMSK1 &= ~_BV(OCIE1A); // Turn off timer
+ TCCR1B &= ~0x7; // Disable timer
+ //_state.timers_running = false;
+ _state.active_timers--;
+
+ if (_state.powered)
+ {
+ debug_log("PWROFF");
+
+ _state.power_off = true;
+ }
+
+ //debug_set(DEBUG_2, true);
+
+ //power_off();
+
+ //sei();
+ pmc_mask_irqs(false);
+
+ //sleep_mode();
+}
+
+ISR(PCINT0_vect)
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ //debug_log("PCINT0");
+
+ // CORE_PGOOD
+ // Assert low: power problem -> shutdown
+ // USBPM_IRQ
+ // Charge status change? -> update LED
+ // Power problem: battery -> blink charge LED
+ // major -> shutdown
+
+ if (/*(_state.powered) && */(/*io_test_pin(CORE_PGOOD)*/tps54478_is_power_good() == false))
+ {
+ _state.core_power_bad = true;
+ }
+#ifdef CHARGER_TI
+ if (bq24190_has_interrupt())
+ {
+ _state.bq24190_irq = true;
+ }
+#else
+ if (ltc4155_has_interrupt())
+ {
+ _state.ltc4155_irq = true;
+ }
+#endif // CHARGER_TI
+ //sei();
+ pmc_mask_irqs(false);
+}
+
+ISR(PCINT2_vect)
+{
+ //cli();
+ pmc_mask_irqs(true);
+
+ //debug_log("PCINT2");
+
+ // PWR_IRQ
+ // Regulator problem: shutdown
+ // PWR_RESET
+ // Ignored
+
+ if (ltc3675_has_interrupt())
+ {
+ //debug_set(IO_PB(6), true);
+ _state.ltc3675_irq = true;
+ }
+
+ //sei();
+ pmc_mask_irqs(false);
+}
diff --git a/firmware/e300/rev_c/power.h b/firmware/e300/rev_c/power.h
new file mode 100644
index 000000000..453633414
--- /dev/null
+++ b/firmware/e300/rev_c/power.h
@@ -0,0 +1,58 @@
+#ifndef POWER_H
+#define POWER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+void tps54478_init(bool enable);
+void tps54478_set_power(bool on); // Zynq core power (1.0V for FPGA)
+bool tps54478_is_power_good(void);
+
+void charge_set_led(bool on); // Here for error blink codes
+void charge_notify(bool charging);
+
+void power_signal_interrupt(void);
+
+void fpga_reset(bool delay);
+
+typedef enum power_subsystems {
+ PS_UNKNOWN,
+ PS_FPGA,
+ PS_VDRAM,
+ PS_PERIPHERALS_1_8,
+ PS_PERIPHERALS_3_3,
+ PS_TX,
+ PS_MAX
+} power_subsystem_t;
+
+enum Regulators
+{
+ REG_UNKNOWN,
+ REG_TPS54478,
+ REG_LTC3675
+};
+
+bool power_enable(power_subsystem_t subsys, bool on);
+
+void battery_init(void);
+uint16_t battery_get_voltage(void); // mV
+
+bool power_init(void);
+bool power_on(void);
+uint8_t power_off(void);
+
+//bool power_is_subsys_on(int8_t index);
+bool power_is_subsys_on(power_subsystem_t index);
+//int8_t power_get_regulator_index(uint8_t device, uint8_t address);
+//bool ltc3675_reg_helper(uint8_t address);
+
+void usbhub_reset(void);
+
+#ifndef I2C_REWORK
+#include "io.h"
+
+extern io_pin_t PWR_SDA;
+extern io_pin_t PWR_SCL;
+#endif // I2C_REWORK
+
+#endif // POWER_H
diff --git a/host/docs/mainpage.dox b/host/docs/mainpage.dox
index 597938c35..17f01cfc0 100644
--- a/host/docs/mainpage.dox
+++ b/host/docs/mainpage.dox
@@ -39,6 +39,7 @@ devices and how to use the API to connect to them through your own software.
## USRP E-Series Devices
\li \subpage page_usrp_e1x0
+\li \subpage page_usrp_e3x0
\li \subpage page_gpsdo
## USRP X-Series Devices
diff --git a/host/docs/usrp_e3x0.dox b/host/docs/usrp_e3x0.dox
new file mode 100644
index 000000000..bfa9ebf11
--- /dev/null
+++ b/host/docs/usrp_e3x0.dox
@@ -0,0 +1,302 @@
+/*! \page page_usrp_e3x0 USRP-E3x0 Series Device Manual
+
+\tableofcontents
+
+\section e3x0_feature_list Comparative features list
+
+- Hardware Capabilities:
+ Integrated RF frontend (70 MHz - 6 GHz)
+ - External PPS reference input
+ - External 10 MHz reference input
+ - Configurable clock rate
+ - Internal GPIO connector with UHD API control
+ - 2 USB 2.0 Host ports
+ - Internal GPSDO
+ - Soundcard mono input / stereo output
+ - USB UART
+ - Zynq-7020 FPGA
+- FPGA Capabilities:
+ - 2 RX DDC chains in FPGA
+ - 2 TX DUC chain in FPGA
+ - Timed commands in FPGA
+ - Timed sampling in FPGA
+ - 16-bit and sample modes (sc16)
+ - Up to 10 MHz of RF bandwidth with 16-bit samples
+
+\section e3x0_getting_started Getting started
+
+This will run you through the first steps relevant to get your USRP E300/310
+up and running.
+
+\subsection e3x0_first_boot First boot
+
+After unpacking and assembling your USRP E300/E310 insert the micro sd card into the micro sd card slot.
+
+There are two different methods to connect to the device
+
+- using the onboard serial to usb connector
+- using the gigabit ethernet connector and a ssh client on your host computer
+
+For the first boot, booting with the serial cable connected to the device
+is recommended, as it allows to review and modify the network configuration,
+and allows to enter the bootloader in case of issues during the boot.
+
+
+\subsubsection e3x0_first_boot_serial Serial connection
+
+To use the serial connection together with a Linux or OSX machine (most other UNIX variants come with a version of screen, too)
+a terminal emulator such as screen can be used:
+
+ $ sudo screen /dev/ttyUSB0 115200
+
+The exact device node /dev/ttyUSB0 depends on your operating system's driver and other USB devices that might be already connected.
+It can be usually found by perusing the output of dmesg or journalctl, after connecting the USRP E300/E310 device to your host computer.
+
+An example of a dmesg output for the serial to usb converter:
+
+ 924.102764] usb 1-1: FTDI USB Serial Device converter now attached to ttyUSB0
+
+
+On Microsoft Windows the serial connection can be established using a tool such as Putty by selecting a baudrate of 115200 and the corresponding serial port for the serial to usb converter.
+
+In both cases you should see boot messages fly by and finally end up with a login prompt similar to the following:
+
+TODO!!
+
+Note: The username is 'root' and the default password is empty.
+
+You should be presented with a shell similar to the following
+
+ root@ettus-e300:~#
+
+
+\subsubsection e3x0_first_boot_ssh SSH connection
+
+The USRP E300/E310 device relies on the DHCP protocol to automatically obtain an IP address.
+In case your network setup does not include a DHCP server, refer to the section \ref e3x0_first_boot_serial or configure a DHCP server to hand out IP addresses on your network.
+
+After the device obtained an IP address you can log in from a Linux or OSX machine by typing:
+
+ $ ssh root@192.168.10.42
+
+where the IP address depends on your local network setup.
+
+On Microsoft Windows again the connection can be established using a tool such as Putty, by selecting a username of root without password.
+
+You should be presented with a shell similar to the following
+
+ root@ettus-e300:~#
+
+\section e3x0_hw Hardware Setup
+
+\section e3x0_load_fpga_imgs Load FPGA Images onto the Device
+
+The USRP-X Series device ships with a bitstream pre-programmed in the flash,
+which is automatically loaded onto the FPGA during device power-up. However,
+a new FPGA image can be configured over the PCI Express interface or the
+on-board USB-JTAG programmer. This process can be seen as a "one-time load", in
+that if you power-cycle the device, it will not retain the FPGA image.
+
+Please note that this process is *different* than replacing the FPGA image
+stored in the flash, which will then be automatically loaded the next time the
+device is reset.
+
+\subsection e3x0_load_fpga_imgs_jtag Use JTAG to load FPGA images
+
+The USRP-E Series device features an on-board JTAG connector that can be accessed on the PCB
+of the device. The iMPACT tool in the <a href="http://www.xilinx.com/support/download/index.htm">Xilinx Programming Tools (ISE, iMPACT)</a> package can be used to load an image over the JTAG interface. This can be useful for unbricking devices.
+
+If you have iMPACT installed, you can use the `impact_jtag_programmer.sh` tool to install images. Make sure your e3x0 is powered on and connected to your computer using the internal JTAG connector. Then run the tool:
+
+ <path_to_uhd_tools>/impact_jtag_programmer.sh --fpga-path=<fpga_image_path>
+
+\subsection e3x0_setup_change_ip Change the USRP's IP address
+
+You may need to change the USRP's IP address for several reasons:
+- to satisfy your particular network configuration
+- to use multiple USRP-E Series devices with the same host computer
+- to set a known IP address into USRP (in case you forgot)
+
+\section e3x0_addressing Addressing the Device
+
+\subsection e3x0_addressing_singledev Single device configuration
+
+In a single-device configuration,
+the USRP device must have a unique IPv4 address on the host computer.
+The USRP can be identified through its IPv4 address or resolvable hostname.
+See the application notes on \ref page_identification.
+Use this addressing scheme with the uhd::usrp::multi_usrp interface (not a typo!).
+
+Example device address string representation for a USRP-E Series device with IPv4 address 192.168.10.2:
+
+ addr=192.168.10.2
+
+\subsection e3x0_addressing_multidevcfg Multiple device configuration
+
+In a multi-device configuration,
+each USRP device must have a unique IPv4 address on the host computer.
+The device address parameter keys must be suffixed with the device index.
+Each parameter key should be of the format \<key\>\<index\>.
+Use this addressing scheme with the uhd::usrp::multi_usrp interface.
+
+- The order in which devices are indexed corresponds to the indexing of the transmit and receive channels.
+- The key indexing provides the same granularity of device identification as in the single device case.
+
+Example device address string representation for 2 USRPs with IPv4 addresses **192.168.10.2** and **192.168.20.2**:
+
+ addr0=192.168.10.2, addr1=192.168.20.2
+
+
+\section e3x0_comm_problems Communication Problems
+
+When setting up a development machine for the first time,
+you may have various difficulties communicating with the USRP device.
+The following tips are designed to help narrow down and diagnose the problem.
+
+\subsection e3x0_comm_problems_runtimeerr RuntimeError: no control response
+
+This is a common error that occurs when you have set the subnet of your network
+interface to a different subnet than the network interface of the USRP device. For
+example, if your network interface is set to **192.168.20.1**, and the USRP device is **192.168.10.2**
+(note the difference in the third numbers of the IP addresses), you
+will likely see a 'no control response' error message.
+
+Fixing this is simple - just set the your host PC's IP address to the same
+subnet as that of your USRP device. Instructions for setting your IP address are in the
+previous section of this documentation.
+
+\subsection e3x0_comm_problems_firewall Firewall issues
+
+When the IP address is not specified,
+the device discovery broadcasts UDP packets from each Ethernet interface.
+Many firewalls will block the replies to these broadcast packets.
+If disabling your system's firewall
+or specifying the IP address yields a discovered device,
+then your firewall may be blocking replies to UDP broadcast packets.
+If this is the case, we recommend that you disable the firewall
+or create a rule to allow all incoming packets with UDP source port **49152**.
+
+\subsection e3x0_comm_problems_ping Ping the device
+The USRP device will reply to ICMP echo requests ("ping").
+A successful ping response means that the device has booted properly
+and that it is using the expected IP address.
+
+ ping 192.168.10.2
+
+\subsection e3x0_comm_problems_monitor Monitor the host network traffic
+Use Wireshark to monitor packets sent to and received from the device.
+
+\subsection e3x0_comm_problems_leds Observe Ethernet port LEDs
+When there is network traffic arriving at the Ethernet port, LEDs will light up.
+You can use this to make sure the network connection is correctly set up, e.g.
+by pinging the USRP and making sure the LEDs start to blink.
+
+\section e3x0_hw Hardware Notes
+
+\subsection e3x0_hw_fpanel Front Panel
+
+\image html e3x0_fp_overlay.png "e3x0"
+
+- **RF A Group**
+ + **TX/RX LED**: Indicates that data is streaming on the TX/RX channel on daughterboard A
+ + **RX2 LED**: Indicates that data is streaming on the RX2 channel on daughterboard A
+- **REF**: Indicates that the external Reference Clock is locked
+- **PPS**: Indicates a valid PPS signal by pulsing once per second
+- **AUX I/O**: Front panel GPIO connector.
+- **GPS**: Indicates that GPS reference is locked
+- **LINK**: Indicates that the host computer is communicating with the device (Activity)
+
+- **RF B Group**
+ + **TX/RX LED**: Indicates that data is streaming on the TX/RX channel on daughterboard B
+ + **RX2 LED**: Indicates that data is streaming on the RX2 channel on daughterboard B
+- **PWR**: Power switch
+
+\subsection e3x0_hw_rear_panel Rear Panel
+
+\image html e3x0_rp_overlay.png "e3x0 Rear Panel"
+
+- **PWR**: Connector for the USRP-X Series power supply
+- **1G/10G ETH**: SFP+ ports for Ethernet interfaces
+- **REF OUT**: Output port for the exported reference clock
+- **REF IN**: Reference clock input
+- **PCIe x4**: Connector for Cabled PCI Express link
+- **PPS/TRIG OUT**: Output port for the PPS signal
+- **PPS/TRIG IN**: Input port for the PPS signal
+- **GPS**: Connection for the GPS antenna
+
+\subsection e3x0_hw_e3x0_hw_ref10M Ref Clock - 10 MHz
+
+Using an external 10 MHz reference clock, a square wave will offer the best phase
+noise performance, but a sinusoid is acceptable. The power level of the reference clock cannot exceed +15 dBm.
+
+\subsection e3x0_hw_pps PPS - Pulse Per Second
+Using a PPS signal for timestamp synchronization requires a square wave signal with the following a 5Vpp amplitude.
+
+To test the PPS input, you can use the following tool from the UHD examples:
+
+- `<args>` are device address arguments (optional if only one USRP device is on your machine)
+
+ cd <install-path>/lib/uhd/examples
+ ./test_pps_input --args=\<args\>
+
+\subsection e3x0_hw_gpsdo Internal GPSDO
+
+Please see \ref page_gpsdo_e3x0 for information on configuring and using the internal GPSDO.
+
+\subsection e3x0_hw_gpio Internal GPIO
+
+### Connector
+
+\image html e3x0_gpio_conn.png "e3x0 GPIO Connector"
+
+### Pin Mapping
+
+- Pin 1: +3.3V
+- Pin 2: Data[0]
+- Pin 3: Data[1]
+- Pin 4: Data[2]
+- Pin 5: Data[3]
+- Pin 6: Data[4]
+- Pin 7: Data[5]
+- Pin 8: Data[6]
+- Pin 9: Data[7]
+- Pin 10: Data[8]
+TODO:
+
+
+Please see the \ref page_gpio_api for information on configuring and using the GPIO bus.
+
+\subsection e3x0_hw_chipscope Debugging custom FPGA designs with Xilinx Chipscope
+
+Xilinx chipscope allows for debugging custom FPGA designs similar to a logic analyzer.
+USRP-E series devices can be used with Xilinx chipscope using the internal JTAG connector.
+
+Further information on how to use Chipscope can be found in the Xilinx Chipscope Pro Software and Cores User Guide (UG029).
+
+\section e3x0_misc Miscellaneous
+
+\subsection e3x0_misc_multirx Multiple RX channels
+
+There are two complete DDC and DUC DSP chains in the FPGA. In the single channel case,
+only one chain is ever used. To receive from both channels, the user must set the **RX** or **TX**
+subdevice specification.
+
+In the following example, a TVRX2 is installed.
+Channel 0 is sourced from subdevice **RX1**,
+and channel 1 is sourced from subdevice **RX2** (**RX1** and **RX2** are antenna connectors on the TVRX2 daughterboard).
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
+usrp->set_rx_subdev_spec("A:RX1 A:RX2");
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+\subsection e3x0_misc_sensors Available Sensors
+
+The following sensors are available for the USRP-E Series motherboards;
+they can be queried through the API.
+
+- **fe_locked** - rx / tx frontend pll locked
+- **temp** - processor temperature value
+- Other sensors are added when the GPSDO is enabled
+
+*/
+// vim:ft=doxygen:
diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt
index 363555f45..bec88b520 100644
--- a/host/lib/convert/CMakeLists.txt
+++ b/host/lib/convert/CMakeLists.txt
@@ -94,8 +94,11 @@ IF(CMAKE_COMPILER_IS_GNUCXX)
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
IF(HAVE_ARM_NEON_H)
+ ENABLE_LANGUAGE(ASM)
+
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/convert_with_neon.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/convert_neon.S
)
ENDIF()
diff --git a/host/lib/convert/convert_neon.S b/host/lib/convert/convert_neon.S
new file mode 100644
index 000000000..8cbe82bde
--- /dev/null
+++ b/host/lib/convert/convert_neon.S
@@ -0,0 +1,37 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+ .arch armv7-a
+ .fpu neon
+ .syntax unified
+ .text
+ .align 2
+ .global neon_item32_sc16_swap_16n
+ .type neon_item32_sc16_swap_16n, %function
+neon_item32_sc16_swap_16n:
+.loop_swap:
+ vld2.16 {q0, q1}, [r0]!
+ vld2.16 {q2, q3}, [r0]!
+ vswp q0, q1
+ vswp q2, q3
+ vst2.16 {q0, q1}, [r1]!
+ vst2.16 {q2, q3}, [r1]!
+ subs r2, #1
+ bne .loop_swap
+ bx lr
+ .size neon_item32_sc16_swap_16n, .-neon_item32_sc16_swap_16n
+ .section .note.GNU-stack,"",%progbits
diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp
index e994d97a6..f1c7773ec 100644
--- a/host/lib/convert/convert_with_neon.cpp
+++ b/host/lib/convert/convert_with_neon.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011-2012 Ettus Research LLC
+// Copyright 2011-2014 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,6 +19,12 @@
#include <uhd/utils/byteswap.hpp>
#include <arm_neon.h>
+extern "C" {
+void neon_item32_sc16_swap_16n(void *, void *, int iter);
+}
+
+static const int SIMD_WIDTH = 16;
+
using namespace uhd::convert;
DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){
@@ -58,3 +64,31 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){
item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor);
}
+
+DECLARE_CONVERTER(sc16, 1, sc16_item32_le, 1, PRIORITY_SIMD){
+ const sc16_t *input = reinterpret_cast<const sc16_t *>(inputs[0]);
+ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);
+
+ size_t i = nsamps / SIMD_WIDTH;
+
+ if (i)
+ neon_item32_sc16_swap_16n((void *) input, (void *) output, i);
+
+ i *= SIMD_WIDTH;
+
+ xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor);
+}
+
+DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_SIMD){
+ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);
+ sc16_t *output = reinterpret_cast<sc16_t *>(outputs[0]);
+
+ size_t i = nsamps / SIMD_WIDTH;
+
+ if (i)
+ neon_item32_sc16_swap_16n((void *) input, (void *) output, i);
+
+ i *= SIMD_WIDTH;
+
+ item32_sc16_to_xx<uhd::wtohx>(input+i, output+i, nsamps-i, scale_factor);
+}
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index c8c2e6a8d..f6788b5ef 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -37,5 +37,6 @@ INCLUDE_SUBDIRECTORY(usrp1)
INCLUDE_SUBDIRECTORY(usrp2)
INCLUDE_SUBDIRECTORY(b100)
INCLUDE_SUBDIRECTORY(e100)
+INCLUDE_SUBDIRECTORY(e300)
INCLUDE_SUBDIRECTORY(x300)
INCLUDE_SUBDIRECTORY(b200)
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index 9e8653608..3c5bb4fa8 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -37,5 +37,6 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx2.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_e3x0.cpp
)
diff --git a/host/lib/usrp/dboard/db_e3x0.cpp b/host/lib/usrp/dboard/db_e3x0.cpp
new file mode 100644
index 000000000..523927d49
--- /dev/null
+++ b/host/lib/usrp/dboard/db_e3x0.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/utils/static.hpp>
+#include <uhd/usrp/dboard_base.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+
+namespace uhd { namespace usrp {
+
+/***********************************************************************
+ * The E310 dboard
+ * AD9361 Interface, thus two subdevs
+ **********************************************************************/
+class e310_dboard : public xcvr_dboard_base{
+public:
+ e310_dboard(ctor_args_t args) : xcvr_dboard_base(args) {}
+
+ ~e310_dboard(void) {}
+};
+
+/***********************************************************************
+ * The E310 dboard
+ * AD9364 Interface, thus one subdev
+ **********************************************************************/
+class e300_dboard : public xcvr_dboard_base{
+public:
+ e300_dboard(ctor_args_t args) : xcvr_dboard_base(args) {}
+
+ ~e300_dboard(void) {}
+};
+
+/***********************************************************************
+ * Register the E310 dboards
+ **********************************************************************/
+static dboard_base::sptr make_e310_dboard(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new e310_dboard(args));
+}
+
+static dboard_base::sptr make_e300_dboard(dboard_base::ctor_args_t args){
+ return dboard_base::sptr(new e300_dboard(args));
+}
+
+}} // namespace
+
+using namespace uhd::usrp;
+
+UHD_STATIC_BLOCK(reg_e3x0_dboards){
+ dboard_manager::register_dboard(0x0110, &make_e310_dboard, "E310 MIMO XCVR");
+ dboard_manager::register_dboard(0x0100, &make_e300_dboard, "E300 SISO XCVR");
+}
diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt
new file mode 100644
index 000000000..9ee9b5521
--- /dev/null
+++ b/host/lib/usrp/e300/CMakeLists.txt
@@ -0,0 +1,55 @@
+#
+# Copyright 2013-2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the USRP-E300 support
+########################################################################
+find_package(UDev)
+
+LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF)
+
+IF(ENABLE_E300)
+ LIST(APPEND E300_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_fifo_config.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_sysfs_hooks.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_network.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_global_regs.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_spi.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_sensor_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_async_serial.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_ublox_control_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp
+ )
+ LIBUHD_APPEND_SOURCES(${E300_SOURCES})
+ IF(UDEV_FOUND)
+ INCLUDE_DIRECTORIES(${UDEV_INCLUDE_DIR})
+ LIBUHD_APPEND_LIBS(${UDEV_LIBS})
+ SET_SOURCE_FILES_PROPERTIES(
+ ${E300_SOURCES}
+ PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1"
+ )
+ ENDIF(UDEV_FOUND)
+ENDIF(ENABLE_E300)
diff --git a/host/lib/usrp/e300/e300_async_serial.cpp b/host/lib/usrp/e300/e300_async_serial.cpp
new file mode 100644
index 000000000..cdf18f7f7
--- /dev/null
+++ b/host/lib/usrp/e300/e300_async_serial.cpp
@@ -0,0 +1,245 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_async_serial.hpp"
+
+namespace uhd { namespace usrp { namespace gps {
+
+async_serial::async_serial()
+ : _io(),
+ _port(_io),
+ _background_thread(),
+ _open(false),
+ _error(false)
+{
+}
+
+async_serial::async_serial(
+ const std::string &node,
+ const size_t baud_rate,
+ boost::asio::serial_port_base::parity opt_parity,
+ boost::asio::serial_port_base::character_size opt_csize,
+ boost::asio::serial_port_base::flow_control opt_flow,
+ boost::asio::serial_port_base::stop_bits opt_stop)
+ : _io(),
+ _port(_io),
+ _background_thread(),
+ _open(false),
+ _error(false)
+{
+ open(node, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop);
+}
+
+void async_serial::open(
+ const std::string &node,
+ const size_t baud_rate,
+ boost::asio::serial_port_base::parity opt_parity,
+ boost::asio::serial_port_base::character_size opt_csize,
+ boost::asio::serial_port_base::flow_control opt_flow,
+ boost::asio::serial_port_base::stop_bits opt_stop)
+{
+ if(is_open())
+ close();
+
+ _set_error_status(true);
+ _port.open(node);
+ _port.set_option(
+ boost::asio::serial_port_base::baud_rate(baud_rate));
+ _port.set_option(opt_parity);
+ _port.set_option(opt_csize);
+ _port.set_option(opt_flow);
+ _port.set_option(opt_stop);
+
+ _io.post(boost::bind(&async_serial::_do_read, this));
+
+ boost::thread t(boost::bind(&boost::asio::io_service::run, &_io));
+ _background_thread.swap(t);
+ _set_error_status(false);
+ _open=true;
+}
+
+bool async_serial::is_open() const
+{
+ return _open;
+}
+
+bool async_serial::error_status() const
+{
+ boost::lock_guard<boost::mutex> l(_error_mutex);
+ return _error;
+}
+
+void async_serial::close()
+{
+ if(!is_open())
+ return;
+
+ _open=false;
+ _io.post(boost::bind(&async_serial::_do_close, this));
+ _background_thread.join();
+ _io.reset();
+ if(error_status())
+ throw(boost::system::system_error(boost::system::error_code(),
+ "Error while closing the device"));
+}
+
+void async_serial::write(const char *data, size_t size)
+{
+ {
+ boost::lock_guard<boost::mutex> l(_write_queue_mutex);
+ _write_queue.insert(_write_queue.end(), data, data+size);
+ }
+ _io.post(boost::bind(&async_serial::_do_write, this));
+}
+
+void async_serial::write(const std::vector<char> &data)
+{
+ {
+ boost::lock_guard<boost::mutex> l(_write_queue_mutex);
+ _write_queue.insert(
+ _write_queue.end(),
+ data.begin(),
+ data.end());
+ }
+ _io.post(boost::bind(&async_serial::_do_write, this));
+}
+
+void async_serial::write_string(const std::string &s)
+{
+ {
+ boost::lock_guard<boost::mutex> l(_write_queue_mutex);
+ _write_queue.insert(
+ _write_queue.end(),
+ s.begin(),
+ s.end());
+ }
+ _io.post(boost::bind(&async_serial::_do_write, this));
+}
+
+async_serial::~async_serial()
+{
+ if(is_open()) {
+ try {
+ close();
+ } catch(...) {
+ //Don't throw from a destructor
+ }
+ }
+}
+
+void async_serial::_do_read()
+{
+ _port.async_read_some(boost::asio::buffer(
+ _read_buffer,READ_BUFFER_SIZE),
+ boost::bind(&async_serial::_read_end,
+ this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+}
+
+void async_serial::_read_end(
+ const boost::system::error_code& error,
+ size_t bytes_transferred)
+{
+ if(error) {
+ if(is_open()) {
+ _do_close();
+ _set_error_status(true);
+ }
+ } else {
+ if(_callback)
+ _callback(
+ _read_buffer,
+ bytes_transferred);
+ _do_read();
+ }
+}
+
+void async_serial::_do_write()
+{
+ // if a write operation is already in progress, do nothing
+ if(_write_buffer == 0) {
+ boost::lock_guard<boost::mutex> l(_write_queue_mutex);
+ _write_buffer_size=_write_queue.size();
+ _write_buffer.reset(new char[_write_queue.size()]);
+ std::copy(_write_queue.begin(),_write_queue.end(),
+ _write_buffer.get());
+ _write_queue.clear();
+ async_write(
+ _port, boost::asio::buffer(_write_buffer.get(),
+ _write_buffer_size),
+ boost::bind(
+ &async_serial::_write_end,
+ this,
+ boost::asio::placeholders::error));
+ }
+}
+
+void async_serial::_write_end(const boost::system::error_code& error)
+{
+ if(!error) {
+ boost::lock_guard<boost::mutex> l(_write_queue_mutex);
+ if(_write_queue.empty()) {
+ _write_buffer.reset();
+ _write_buffer_size=0;
+ return;
+ }
+ _write_buffer_size = _write_queue.size();
+ _write_buffer.reset(new char[_write_queue.size()]);
+ std::copy(_write_queue.begin(),_write_queue.end(),
+ _write_buffer.get());
+ _write_queue.clear();
+ async_write(
+ _port,
+ boost::asio::buffer(_write_buffer.get(),
+ _write_buffer_size),
+ boost::bind(
+ &async_serial::_write_end,
+ this,
+ boost::asio::placeholders::error));
+ } else {
+ _set_error_status(true);
+ _do_close();
+ }
+}
+
+void async_serial::_do_close()
+{
+ boost::system::error_code ec;
+ _port.cancel(ec);
+ if(ec)
+ _set_error_status(true);
+ _port.close(ec);
+ if(ec)
+ _set_error_status(true);
+}
+
+void async_serial::_set_error_status(const bool e)
+{
+ boost::lock_guard<boost::mutex> l(_error_mutex);
+ _error = e;
+}
+
+
+void async_serial::set_read_callback(
+ const boost::function<void (const char*, size_t)> &callback)
+{
+ _callback = callback;
+}
+
+
+}}} // namespace
diff --git a/host/lib/usrp/e300/e300_async_serial.hpp b/host/lib/usrp/e300/e300_async_serial.hpp
new file mode 100644
index 000000000..fafc7de3d
--- /dev/null
+++ b/host/lib/usrp/e300/e300_async_serial.hpp
@@ -0,0 +1,113 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_ASYNC_SERIAL_HPP
+#define INCLUDED_ASYNC_SERIAL_HPP
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <boost/utility.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_array.hpp>
+
+namespace uhd { namespace usrp { namespace gps {
+
+class async_serial : private boost::noncopyable
+{
+public:
+ async_serial();
+ ~async_serial();
+
+ async_serial(const std::string &node, const size_t baud_rate,
+ boost::asio::serial_port_base::parity opt_parity=
+ boost::asio::serial_port_base::parity(
+ boost::asio::serial_port_base::parity::none),
+ boost::asio::serial_port_base::character_size opt_csize=
+ boost::asio::serial_port_base::character_size(8),
+ boost::asio::serial_port_base::flow_control opt_flow=
+ boost::asio::serial_port_base::flow_control(
+ boost::asio::serial_port_base::flow_control::none),
+ boost::asio::serial_port_base::stop_bits opt_stop=
+ boost::asio::serial_port_base::stop_bits(
+ boost::asio::serial_port_base::stop_bits::one));
+
+ void open(const std::string& node, const size_t baud_rate,
+ boost::asio::serial_port_base::parity opt_parity=
+ boost::asio::serial_port_base::parity(
+ boost::asio::serial_port_base::parity::none),
+ boost::asio::serial_port_base::character_size opt_csize=
+ boost::asio::serial_port_base::character_size(8),
+ boost::asio::serial_port_base::flow_control opt_flow=
+ boost::asio::serial_port_base::flow_control(
+ boost::asio::serial_port_base::flow_control::none),
+ boost::asio::serial_port_base::stop_bits opt_stop=
+ boost::asio::serial_port_base::stop_bits(
+ boost::asio::serial_port_base::stop_bits::one));
+
+ bool is_open(void) const;
+
+ bool error_status(void) const;
+
+ void close(void);
+
+ void write(const char *data, const size_t size);
+ void write(const std::vector<char> &data);
+
+ void write_string(const std::string &s);
+
+ static const size_t READ_BUFFER_SIZE=512;
+
+ void set_read_callback(
+ const boost::function<void (const char*, size_t)>& callback);
+
+ void clear_callback();
+
+private: // methods
+ void _do_read();
+
+ void _read_end(
+ const boost::system::error_code &error,
+ size_t bytes_transferred);
+
+ void _do_write();
+
+ void _write_end(const boost::system::error_code &error);
+
+ void _do_close();
+
+ void _set_error_status(const bool e);
+private: // members
+ boost::asio::io_service _io;
+ boost::asio::serial_port _port;
+ boost::thread _background_thread;
+ bool _open;
+ bool _error;
+ mutable boost::mutex _error_mutex;
+
+ std::vector<char> _write_queue;
+ boost::shared_array<char> _write_buffer;
+ size_t _write_buffer_size;
+ boost::mutex _write_queue_mutex;
+ char _read_buffer[READ_BUFFER_SIZE];
+
+ boost::function<void (const char*, size_t)> _callback;
+};
+
+}}} // namespace
+
+#endif //INCLUDED_ASYNC_SERIAL_HPP
diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp
new file mode 100644
index 000000000..97e906be7
--- /dev/null
+++ b/host/lib/usrp/e300/e300_common.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#include <uhd/utils/msg.hpp>
+
+#include "e300_fifo_config.hpp"
+#include "e300_fifo_config.hpp"
+
+#include "e300_common.hpp"
+
+#include <boost/filesystem.hpp>
+#include <fstream>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+namespace common {
+
+void load_fpga_image(const std::string &path)
+{
+ if (not boost::filesystem::exists("/dev/xdevcfg"))
+ ::system("mknod /dev/xdevcfg c 259 0");
+
+ UHD_MSG(status) << "Loading FPGA image: " << path << "..." << std::flush;
+
+ std::ifstream fpga_file(path.c_str(), std::ios_base::binary);
+ UHD_ASSERT_THROW(fpga_file.good());
+
+ std::FILE *wfile;
+ wfile = std::fopen("/dev/xdevcfg", "wb");
+ UHD_ASSERT_THROW(!(wfile == NULL));
+
+ char buff[16384]; // devcfg driver can't handle huge writes
+ do {
+ fpga_file.read(buff, sizeof(buff));
+ std::fwrite(buff, 1, fpga_file.gcount(), wfile);
+ } while (fpga_file);
+
+ fpga_file.close();
+ std::fclose(wfile);
+
+ UHD_MSG(status) << " done" << std::endl;
+}
+
+}
+
+}}}
diff --git a/host/lib/usrp/e300/e300_common.hpp b/host/lib/usrp/e300/e300_common.hpp
new file mode 100644
index 000000000..d9a0afd9e
--- /dev/null
+++ b/host/lib/usrp/e300/e300_common.hpp
@@ -0,0 +1,31 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_COMMON_HPP
+#define INCLUDED_E300_COMMON_HPP
+
+namespace uhd { namespace usrp { namespace e300 {
+
+namespace common {
+
+void load_fpga_image(const std::string &path);
+
+};
+
+}}}
+
+#endif // INCLUDED_E300_COMMON_HPP
diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp
new file mode 100644
index 000000000..9c0e5df1d
--- /dev/null
+++ b/host/lib/usrp/e300/e300_defaults.hpp
@@ -0,0 +1,82 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_DEFAULTS_HPP
+#define INCLUDED_E300_DEFAULTS_HPP
+
+#include "ad9361_client.h"
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const double DEFAULT_TICK_RATE = 32e6;
+static const double MAX_TICK_RATE = 50e6;
+static const double MIN_TICK_RATE = 1e6;
+
+static const double DEFAULT_TX_SAMP_RATE = 1.0e6;
+static const double DEFAULT_RX_SAMP_RATE = 1.0e6;
+static const double DEFAULT_DDC_FREQ = 0.0;
+static const double DEFAULT_DUC_FREQ = 0.0;
+
+static const double DEFAULT_FE_GAIN = 0.0;
+static const double DEFAULT_FE_FREQ = 1.0e9;
+static const double DEFAULT_FE_BW = 56e6;
+
+static const std::string DEFAULT_TIME_SRC = "none";
+static const std::string DEFAULT_CLOCK_SRC = "internal";
+
+static const size_t DEFAULT_RX_DATA_FRAME_SIZE = 4096;
+static const size_t DEFAULT_RX_DATA_NUM_FRAMES = 32;
+
+static const size_t DEFAULT_TX_DATA_FRAME_SIZE = 2048;
+static const size_t DEFAULT_TX_DATA_NUM_FRAMES = 32;
+
+static const size_t DEFAULT_CTRL_FRAME_SIZE = 64;
+static const size_t DEFAULT_CTRL_NUM_FRAMES = 32;
+
+static const size_t MAX_NET_RX_DATA_FRAME_SIZE = 1200;
+static const size_t MAX_NET_TX_DATA_FRAME_SIZE = 1200;
+
+class e300_ad9361_client_t : public ad9361_params {
+public:
+ ~e300_ad9361_client_t() {}
+ double get_band_edge(frequency_band_t band) {
+ switch (band) {
+ case AD9361_RX_BAND0: return 1.2e9;
+ case AD9361_RX_BAND1: return 2.6e9;
+ case AD9361_TX_BAND0: return 2940.0e6;
+ default: return 0;
+ }
+ }
+ clocking_mode_t get_clocking_mode() {
+ return AD9361_XTAL_N_CLK_PATH;
+ }
+ digital_interface_mode_t get_digital_interface_mode() {
+ return AD9361_DDR_FDD_LVCMOS;
+ }
+ digital_interface_delays_t get_digital_interface_timing() {
+ digital_interface_delays_t delays;
+ delays.rx_clk_delay = 0;
+ delays.rx_data_delay = 0xF;
+ delays.tx_clk_delay = 0;
+ delays.tx_data_delay = 0xF;
+ return delays;
+ }
+};
+
+}}} // namespace
+
+#endif // INCLUDED_E300_DEFAULTS_HPP
diff --git a/host/lib/usrp/e300/e300_eeprom_manager.cpp b/host/lib/usrp/e300/e300_eeprom_manager.cpp
new file mode 100644
index 000000000..af1235b6b
--- /dev/null
+++ b/host/lib/usrp/e300/e300_eeprom_manager.cpp
@@ -0,0 +1,236 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_eeprom_manager.hpp"
+#include <uhd/types/mac_addr.hpp>
+#include <uhd/utils/byteswap.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len)
+{
+ std::string out;
+ for (size_t i = 0; i < max_len; i++) {
+ if (bytes[i] < 32 or bytes[i] > 127) return out;
+ out += bytes[i];
+ }
+ return out;
+}
+
+static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer)
+{
+ byte_vector_t bytes;
+ const size_t len = std::min(string.size(), max_len);
+ for (size_t i = 0; i < len; i++){
+ buffer[i] = string[i];
+ }
+ if (len < max_len - 1)
+ buffer[len] = '\0';
+}
+
+e300_eeprom_manager::e300_eeprom_manager(i2c::sptr i2c) : _i2c(i2c)
+{
+ read_mb_eeprom();
+ read_db_eeprom();
+}
+
+e300_eeprom_manager::~e300_eeprom_manager(void)
+{
+}
+
+const mboard_eeprom_t& e300_eeprom_manager::read_mb_eeprom(void)
+{
+ boost::mutex::scoped_lock(_mutex);
+
+ std::vector<boost::uint8_t> bytes;
+ bytes.resize(sizeof(mb_eeprom_map_t));
+ mb_eeprom_map_t *map_ptr = reinterpret_cast<mb_eeprom_map_t*>(&bytes[0]);
+ memset(map_ptr, 0xff, sizeof(mb_eeprom_map_t));
+
+ // get the old contents
+ for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++)
+ bytes[i] = _i2c->get_i2c_reg8(MB_ADDR, i);
+
+ mb_eeprom_map_t &map = *map_ptr;
+
+ _mb_eeprom["product"] = boost::lexical_cast<std::string>(
+ uhd::ntohx<boost::uint16_t>(map.hw_product));
+ _mb_eeprom["revision"] = boost::lexical_cast<std::string>(
+ uhd::ntohx<boost::uint16_t>(map.hw_revision));
+ _mb_eeprom["serial"] = _bytes_to_string(
+ map.serial, MB_SERIAL_LEN);
+
+ byte_vector_t mac_addr(map.mac_addr, map.mac_addr + 6);
+ _mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(mac_addr).to_string();
+
+ _mb_eeprom["name"] = _bytes_to_string(
+ map.user_name, MB_NAME_LEN);
+
+ return _mb_eeprom;
+}
+
+const dboard_eeprom_t& e300_eeprom_manager::read_db_eeprom(void)
+{
+ boost::mutex::scoped_lock(_mutex);
+
+ std::vector<boost::uint8_t> bytes;
+ bytes.resize(sizeof(db_eeprom_map_t));
+ db_eeprom_map_t *map_ptr = reinterpret_cast<db_eeprom_map_t*>(&bytes[0]);
+ memset(map_ptr, 0xff, sizeof(db_eeprom_map_t));
+
+ // get the old contents
+ for(size_t i = 0; i < sizeof(db_eeprom_map_t); i++)
+ bytes[i] = _i2c->get_i2c_reg16(DB_ADDR, i);
+
+ db_eeprom_map_t &map = *map_ptr;
+
+ _db_eeprom.id = uhd::usrp::dboard_id_t::from_uint16(
+ uhd::ntohx<boost::uint16_t>(map.hw_product));
+
+ _db_eeprom.revision = boost::lexical_cast<std::string>(
+ uhd::ntohx<boost::uint16_t>(map.hw_revision));
+ _db_eeprom.serial = _bytes_to_string(
+ map.serial, DB_SERIAL_LEN);
+
+ return _db_eeprom;
+}
+
+void e300_eeprom_manager::write_db_eeprom(const dboard_eeprom_t& eeprom)
+{
+ boost::mutex::scoped_lock(_mutex);
+ _db_eeprom = eeprom;
+ std::vector<boost::uint8_t> bytes;
+ bytes.resize(sizeof(db_eeprom_map_t));
+
+
+ db_eeprom_map_t *map_ptr = reinterpret_cast<db_eeprom_map_t*>(&bytes[0]);
+ memset(map_ptr, 0xff, sizeof(db_eeprom_map_t));
+
+ // get the old contents
+ for(size_t i = 0; i < sizeof(db_eeprom_map_t); i++)
+ bytes[i] = _i2c->get_i2c_reg16(DB_ADDR, i);
+
+ db_eeprom_map_t &map = *map_ptr;
+
+ if (_db_eeprom.id != dboard_id_t::none()) {
+ map.hw_product = uhd::htonx<boost::uint16_t>(
+ _db_eeprom.id.to_uint16());
+ }
+
+ if (not _db_eeprom.revision.empty()) {
+ map.hw_revision = uhd::htonx<boost::uint16_t>(
+ boost::lexical_cast<boost::uint16_t>(_db_eeprom.revision));
+ }
+
+ if (not _db_eeprom.serial.empty()) {
+ _string_to_bytes(_db_eeprom.serial, DB_SERIAL_LEN, map.serial);
+ }
+ for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++)
+ _i2c->set_i2c_reg16(DB_ADDR, i, bytes[i]);
+}
+
+void e300_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom)
+{
+ boost::mutex::scoped_lock(_mutex);
+ _mb_eeprom = eeprom;
+ std::vector<boost::uint8_t> bytes;
+ bytes.resize(sizeof(mb_eeprom_map_t));
+
+
+ mb_eeprom_map_t *map_ptr = reinterpret_cast<mb_eeprom_map_t*>(&bytes[0]);
+ memset(map_ptr, 0xff, sizeof(mb_eeprom_map_t));
+
+ // get the old contents
+ for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++)
+ bytes[i] = _i2c->get_i2c_reg8(MB_ADDR, i);
+
+ mb_eeprom_map_t &map = *map_ptr;
+
+ if (_mb_eeprom.has_key("product")) {
+ map.hw_product = uhd::htonx<boost::uint16_t>(
+ boost::lexical_cast<boost::uint16_t>(_mb_eeprom["product"]));
+ }
+ if (_mb_eeprom.has_key("revision")) {
+ map.hw_revision = uhd::htonx<boost::uint16_t>(
+ boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision"]));
+ }
+ if (_mb_eeprom.has_key("serial")) {
+ _string_to_bytes(_mb_eeprom["serial"], MB_SERIAL_LEN, map.serial);
+ }
+ if (_mb_eeprom.has_key("mac-addr")) {
+ byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"]).to_bytes();
+ std::copy(mac_addr.begin(), mac_addr.end(), map.mac_addr);
+ }
+
+ //store the name
+ if (_mb_eeprom.has_key("name")) {
+ _string_to_bytes(_mb_eeprom["name"], MB_NAME_LEN, map.user_name);
+ }
+
+ for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++)
+ _i2c->set_i2c_reg8(MB_ADDR, i, bytes[i]);
+
+}
+
+e300_eeprom_manager::mboard_t e300_eeprom_manager::get_mb_type(void) const
+{
+ boost::mutex::scoped_lock(_mutex);
+ boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
+ _mb_eeprom["product"]);
+ return get_mb_type(pid);
+}
+
+e300_eeprom_manager::mboard_t e300_eeprom_manager::get_mb_type(
+ boost::uint16_t pid)
+{
+ switch (pid) {
+ case E300_MB_PID:
+ return USRP_E300_MB;
+
+ case E310_MB_PID:
+ return USRP_E310_MB;
+
+ default:
+ return UNKNOWN;
+ };
+}
+
+
+std::string e300_eeprom_manager::get_mb_type_string(void) const
+{
+ boost::mutex::scoped_lock(_mutex);
+ boost::uint16_t product = boost::lexical_cast<boost::uint16_t>(
+ _mb_eeprom["product"]);
+ switch (product) {
+ case E300_MB_PID:
+ return "E300";
+
+ case E310_MB_PID:
+ return "E310";
+
+ default:
+ return "UNKNOWN";
+ };
+}
+
+i2c::sptr e300_eeprom_manager::get_i2c_sptr(void)
+{
+ return _i2c;
+}
+
+
+}}} // namespace
diff --git a/host/lib/usrp/e300/e300_eeprom_manager.hpp b/host/lib/usrp/e300/e300_eeprom_manager.hpp
new file mode 100644
index 000000000..e77f25ed5
--- /dev/null
+++ b/host/lib/usrp/e300/e300_eeprom_manager.hpp
@@ -0,0 +1,125 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_EEPROM_MANAGER_HPP
+#define INCLUDED_E300_EEPROM_MANAGER_HPP
+
+#include <boost/thread/mutex.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+
+#include "e300_i2c.hpp"
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const boost::uint16_t E300_MB_PID = 0x77d1;
+static const boost::uint16_t E310_MB_PID = 0x77d2;
+
+static const boost::uint16_t E300_DB_PID = 0x0100;
+static const boost::uint16_t E310_DB_PID = 0x0110;
+
+class e300_eeprom_manager : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<e300_eeprom_manager> sptr;
+ e300_eeprom_manager(i2c::sptr i2c);
+ ~e300_eeprom_manager();
+
+ // mboard
+ const mboard_eeprom_t& read_mb_eeprom();
+ void write_mb_eeprom(const mboard_eeprom_t& eeprom);
+
+ UHD_INLINE const mboard_eeprom_t& get_mb_eeprom()
+ {
+ return _mb_eeprom;
+ }
+
+ // dboard
+ const dboard_eeprom_t& read_db_eeprom();
+ void write_db_eeprom(const dboard_eeprom_t& eeprom);
+
+ UHD_INLINE const dboard_eeprom_t& get_db_eeprom()
+ {
+ return _db_eeprom;
+ }
+
+
+ i2c::sptr get_i2c_sptr(void);
+
+ enum mboard_t {USRP_E300_MB, USRP_E310_MB, UNKNOWN};
+
+ mboard_t get_mb_type(void) const;
+ static mboard_t get_mb_type(boost::uint16_t pid);
+ std::string get_mb_type_string(void) const;
+
+private: // types
+ const static size_t MB_SERIAL_LEN = 6;
+ const static size_t MB_NAME_LEN = 32;
+ const static size_t MB_ADDR = 0x51;
+
+ const static size_t DB_SERIAL_LEN = 6;
+ const static size_t DB_ADDR = 0x50;
+
+ struct mb_eeprom_map_t
+ {
+ // Data format version
+ boost::uint16_t data_version_major;
+ boost::uint16_t data_version_minor;
+
+ // NIC mac address
+ boost::uint8_t mac_addr[6];
+
+ // HW identification info
+ boost::uint16_t hw_product;
+ boost::uint16_t hw_revision;
+
+ // serial
+ boost::uint8_t serial[MB_SERIAL_LEN];
+ boost::uint8_t pad[20 - MB_SERIAL_LEN];
+
+ //User specific
+ boost::uint8_t user_name[MB_NAME_LEN];
+ };
+
+ struct db_eeprom_map_t
+ {
+ // Data format version
+ boost::uint16_t data_version_major;
+ boost::uint16_t data_version_minor;
+
+ // HW identification info
+ boost::uint16_t hw_product;
+ boost::uint16_t hw_revision;
+
+ // serial
+ boost::uint8_t serial[MB_SERIAL_LEN];
+ boost::uint8_t pad[20 - MB_SERIAL_LEN];
+ };
+
+private: // members
+ mboard_eeprom_t _mb_eeprom;
+ dboard_eeprom_t _db_eeprom;
+ i2c::sptr _i2c;
+
+ boost::mutex _mutex;
+};
+
+}}} //namespace
+
+#endif // INCLUDED_E300_EEPROM_MANAGER_HPP
diff --git a/host/lib/usrp/e300/e300_fifo_config.cpp b/host/lib/usrp/e300/e300_fifo_config.cpp
new file mode 100644
index 000000000..ac4ace7f2
--- /dev/null
+++ b/host/lib/usrp/e300/e300_fifo_config.cpp
@@ -0,0 +1,429 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifdef E300_NATIVE
+
+#include <boost/cstdint.hpp>
+#include <uhd/config.hpp>
+
+// constants coded into the fpga parameters
+static const size_t ZF_CONFIG_BASE = 0x40000000;
+static const size_t ZF_PAGE_WIDTH = 10;
+static const size_t H2S_STREAMS_WIDTH = 4;
+static const size_t H2S_CMDFIFO_DEPTH = 5;
+static const size_t S2H_STREAMS_WIDTH = 4;
+static const size_t S2H_CMDFIFO_DEPTH = 5;
+
+// calculate more useful constants for this module
+static const size_t ZF_PAGE_SIZE(1 << ZF_PAGE_WIDTH);
+static const size_t H2S_NUM_STREAMS(1 << H2S_STREAMS_WIDTH);
+static const size_t H2S_NUM_CMDS(1 << H2S_CMDFIFO_DEPTH);
+static const size_t S2H_NUM_STREAMS(1 << S2H_STREAMS_WIDTH);
+static const size_t S2H_NUM_CMDS(1 << S2H_CMDFIFO_DEPTH);
+
+//offsetsinto the arbiter memory map
+static const size_t ARBITER_WR_CLEAR = 0;
+static const size_t ARBITER_RD_SIG = 0;
+static const size_t ARBITER_WR_ADDR = 4;
+static const size_t ARBITER_WR_SIZE = 8;
+static const size_t ARBITER_WR_STS_RDY = 12;
+static const size_t ARBITER_WR_STS = 16;
+static const size_t ARBITER_RB_STATUS = 16;
+static const size_t ARBITER_RB_STATUS_OCC = 20;
+static const size_t ARBITER_RB_ADDR_SPACE = 24;
+static const size_t ARBITER_RB_SIZE_SPACE = 28;
+
+// registers for the wb32_iface
+static const size_t SR_CORE_READBACK = 0;
+
+
+static UHD_INLINE size_t S2H_BASE(const size_t base)
+{
+ return base + ZF_PAGE_SIZE * 0;
+}
+
+static UHD_INLINE size_t H2S_BASE(const size_t base)
+{
+ return base + ZF_PAGE_SIZE * 1;
+}
+
+static UHD_INLINE size_t REG_BASE(const size_t base)
+{
+ return base + ZF_PAGE_SIZE * 2;
+}
+
+static UHD_INLINE size_t DST_BASE(const size_t base)
+{
+ return base + ZF_PAGE_SIZE * 3;
+}
+
+static UHD_INLINE size_t ZF_STREAM_OFF(const size_t which)
+{
+ return which * 32;
+}
+
+#include "e300_fifo_config.hpp"
+#include <sys/mman.h> //mmap
+#include <fcntl.h> //open, close
+#include <poll.h> //poll
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <uhd/types/time_spec.hpp> //timeout
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/atomic.hpp>
+
+//locking stuff for shared irq
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+struct e300_fifo_poll_waiter
+{
+ e300_fifo_poll_waiter(const int fd):
+ fd(fd)
+ {
+ //NOP
+ }
+
+ void wait(const double timeout)
+ {
+ if (_poll_claimed.cas(1, 0))
+ {
+ boost::mutex::scoped_lock l(mutex);
+ cond.wait(l);
+ }
+ else
+ {
+ struct pollfd fds[1];
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ ::poll(fds, 1, long(timeout*1000));
+ if (fds[0].revents & POLLIN)
+ ::read(fd, NULL, 0);
+
+ _poll_claimed.write(0);
+ cond.notify_all();
+ }
+ }
+
+ uhd::atomic_uint32_t _poll_claimed;
+ boost::condition_variable cond;
+ boost::mutex mutex;
+ int fd;
+};
+
+static const size_t DEFAULT_FRAME_SIZE = 2048;
+static const size_t DEFAULT_NUM_FRAMES = 32;
+
+using namespace uhd;
+using namespace uhd::transport;
+
+struct __mem_addrz_t
+{
+ size_t which, phys, data, ctrl;
+};
+
+/***********************************************************************
+ * peek n' poke mmapped space
+ **********************************************************************/
+UHD_INLINE void zf_poke32(const boost::uint32_t addr, const boost::uint32_t data)
+{
+ volatile boost::uint32_t *p = reinterpret_cast<boost::uint32_t *>(addr);
+ *p = data;
+}
+
+UHD_INLINE boost::uint32_t zf_peek32(const boost::uint32_t addr)
+{
+ volatile const boost::uint32_t *p = reinterpret_cast<const boost::uint32_t *>(addr);
+ return *p;
+}
+
+/***********************************************************************
+ * managed buffer
+ **********************************************************************/
+struct e300_fifo_mb : managed_buffer
+{
+ e300_fifo_mb(const __mem_addrz_t &addrs, const size_t len):
+ ctrl_base(addrs.ctrl), phys_mem(addrs.phys), mem((void *)addrs.data), len(len){}
+
+ void release(void)
+ {
+ UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_ADDR_SPACE) > 0);
+ UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_SIZE_SPACE) > 0);
+ zf_poke32(ctrl_base + ARBITER_WR_ADDR, phys_mem);
+ zf_poke32(ctrl_base + ARBITER_WR_SIZE, this->size());
+ }
+
+ template <typename T>
+ UHD_INLINE typename T::sptr get_new(void)
+ {
+ return make(reinterpret_cast<T *>(this), mem, len);
+ }
+
+ const size_t ctrl_base;
+ const size_t phys_mem;
+ void *const mem;
+ const size_t len;
+};
+
+/***********************************************************************
+ * transport
+ **********************************************************************/
+class e300_transport : public zero_copy_if
+{
+public:
+ e300_transport(
+ boost::shared_ptr<void> allocator,
+ const __mem_addrz_t &addrs,
+ const size_t num_frames,
+ const size_t frame_size,
+ e300_fifo_poll_waiter *waiter,
+ const bool auto_release
+ ):
+ _allocator(allocator),
+ _addrs(addrs),
+ _num_frames(num_frames),
+ _frame_size(frame_size),
+ _index(0),
+ _waiter(waiter)
+ {
+ //UHD_MSG(status) << boost::format("phys 0x%x") % addrs.phys << std::endl;
+ //UHD_MSG(status) << boost::format("data 0x%x") % addrs.data << std::endl;
+ //UHD_MSG(status) << boost::format("ctrl 0x%x") % addrs.ctrl << std::endl;
+
+ const boost::uint32_t sig = zf_peek32(_addrs.ctrl + ARBITER_RD_SIG);
+ UHD_ASSERT_THROW((sig >> 16) == 0xACE0);
+
+ zf_poke32(_addrs.ctrl + ARBITER_WR_CLEAR, 1);
+ for (size_t i = 0; i < num_frames; i++)
+ {
+ //create a managed buffer at the given offset
+ __mem_addrz_t mb_addrs = addrs;
+ mb_addrs.phys += (i*frame_size);
+ mb_addrs.data += (i*frame_size);
+ boost::shared_ptr<e300_fifo_mb> mb(new e300_fifo_mb(mb_addrs, frame_size));
+
+ //setup the buffers so they are "positioned for use"
+ const size_t sts_good = (1 << 7) | (_addrs.which & 0xf);
+ if (auto_release) mb->get_new<managed_recv_buffer>(); //release for read
+ else zf_poke32(_addrs.ctrl + ARBITER_WR_STS, sts_good); //poke an ok into the sts fifo
+
+ _buffs.push_back(mb);
+ }
+ }
+
+ ~e300_transport(void)
+ {
+ //NOP
+ }
+
+ template <typename T>
+ UHD_INLINE typename T::sptr get_buff(const double timeout)
+ {
+ const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout);
+ do
+ {
+ if (zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS_OCC))
+ {
+ const boost::uint32_t sts = zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS);
+ UHD_ASSERT_THROW((sts >> 7) & 0x1); //assert OK
+ UHD_ASSERT_THROW((sts & 0xf) == _addrs.which); //expected tag
+ zf_poke32(_addrs.ctrl + ARBITER_WR_STS_RDY, 1); //pop from sts fifo
+ if (_index == _num_frames)
+ _index = 0;
+ return _buffs[_index++]->get_new<T>();
+ }
+ _waiter->wait(timeout);
+ //boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+ while (time_spec_t::get_system_time() < exit_time);
+
+ return typename T::sptr();
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(const double timeout)
+ {
+ return this->get_buff<managed_recv_buffer>(timeout);
+ }
+
+ size_t get_num_recv_frames(void) const
+ {
+ return _num_frames;
+ }
+
+ size_t get_recv_frame_size(void) const
+ {
+ return _frame_size;
+ }
+
+ managed_send_buffer::sptr get_send_buff(const double timeout)
+ {
+ return this->get_buff<managed_send_buffer>(timeout);
+ }
+
+ size_t get_num_send_frames(void) const
+ {
+ return _num_frames;
+ }
+
+ size_t get_send_frame_size(void) const
+ {
+ return _frame_size;
+ }
+
+private:
+ boost::shared_ptr<void> _allocator;
+ const __mem_addrz_t _addrs;
+ const size_t _num_frames;
+ const size_t _frame_size;
+ size_t _index;
+ e300_fifo_poll_waiter *_waiter;
+ std::vector<boost::shared_ptr<e300_fifo_mb> > _buffs;
+};
+
+/***********************************************************************
+ * memory mapping
+ **********************************************************************/
+class e300_fifo_interface_impl : public virtual e300_fifo_interface
+{
+public:
+ e300_fifo_interface_impl(const e300_fifo_config_t &config):
+ _config(config),
+ _bytes_in_use(0),
+ _recv_entries_in_use(std::vector<size_t>(S2H_NUM_STREAMS, 0)),
+ _send_entries_in_use(std::vector<size_t>(H2S_NUM_STREAMS, 0))
+ {
+ //open the file descriptor to our kernel module
+ const std::string dev = "/dev/axi_fpga";
+ _fd = ::open(dev.c_str(), O_RDWR|O_SYNC);
+ if (_fd < 0)
+ {
+ throw uhd::runtime_error("e300: failed to open " + dev);
+ }
+
+ //mmap the control and data regions into virtual space
+ //UHD_VAR(_config.ctrl_length);
+ //UHD_VAR(_config.buff_length);
+ //UHD_VAR(_config.phys_addr);
+ _buff = ::mmap(NULL, _config.ctrl_length + _config.buff_length, PROT_READ|PROT_WRITE, MAP_SHARED, _fd, 0);
+ if (_buff == MAP_FAILED)
+ {
+ ::close(_fd);
+ throw uhd::runtime_error("e300: failed to mmap " + dev);
+ }
+
+ //segment the memory according to zynq fifo arbiter
+ _ctrl_space = size_t(_buff);
+ _data_space = size_t(_buff) + _config.ctrl_length;
+
+ //zero out the data region
+ std::memset((void *)_data_space, 0, _config.buff_length);
+
+ //create a poll _waiter for the transports
+ _waiter = new e300_fifo_poll_waiter(_fd);
+ }
+
+ virtual ~e300_fifo_interface_impl(void)
+ {
+ delete _waiter;
+ UHD_LOG << "cleanup: munmap" << std::endl;
+ ::munmap(_buff, _config.ctrl_length + _config.buff_length);
+ ::close(_fd);
+ }
+
+ uhd::transport::zero_copy_if::sptr make_recv_xport(
+ const size_t which_stream,
+ const uhd::transport::zero_copy_xport_params &params)
+ {
+ return this->_make_xport(which_stream, params, true);
+ }
+
+ uhd::transport::zero_copy_if::sptr make_send_xport(
+ const size_t which_stream,
+ const uhd::transport::zero_copy_xport_params &params)
+ {
+ return this->_make_xport(which_stream, params, false);
+ }
+
+ size_t get_global_regs_base() const
+ {
+ return REG_BASE(_ctrl_space);
+ }
+
+private:
+ uhd::transport::zero_copy_if::sptr _make_xport(
+ const size_t which_stream,
+ const uhd::transport::zero_copy_xport_params &params,
+ const bool is_recv)
+ {
+ boost::mutex::scoped_lock lock(_setup_mutex);
+
+ const size_t frame_size = is_recv ? params.recv_frame_size : params.send_frame_size;
+ const size_t num_frames = is_recv ? params.num_recv_frames : params.num_send_frames;
+ size_t &entries_in_use = (is_recv)? _recv_entries_in_use.at(which_stream)
+ : _send_entries_in_use.at(which_stream);
+
+ __mem_addrz_t addrs;
+ addrs.which = which_stream;
+ addrs.phys = _config.phys_addr + _bytes_in_use;
+ addrs.data = _data_space + _bytes_in_use;
+ addrs.ctrl = ((is_recv)? S2H_BASE(_ctrl_space) : H2S_BASE(_ctrl_space)) + ZF_STREAM_OFF(which_stream);
+
+ uhd::transport::zero_copy_if::sptr xport;
+ if (is_recv) xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv));
+ else xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv));
+
+ _bytes_in_use += num_frames*frame_size;
+ entries_in_use += num_frames;
+
+ UHD_ASSERT_THROW(_recv_entries_in_use.at(which_stream) <= S2H_NUM_CMDS);
+ UHD_ASSERT_THROW(_send_entries_in_use.at(which_stream) <= H2S_NUM_CMDS);
+ UHD_ASSERT_THROW(_bytes_in_use <= _config.buff_length);
+
+
+ return xport;
+ }
+
+ e300_fifo_config_t _config;
+ e300_fifo_poll_waiter *_waiter;
+ size_t _bytes_in_use;
+ int _fd;
+ void *_buff;
+ size_t _ctrl_space;
+ size_t _data_space;
+ std::vector<size_t> _recv_entries_in_use;
+ std::vector<size_t> _send_entries_in_use;
+ boost::mutex _setup_mutex;
+};
+
+e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &config)
+{
+ return e300_fifo_interface::sptr(new e300_fifo_interface_impl(config));
+}
+
+#else //E300_NATIVE
+
+#include "e300_fifo_config.hpp"
+#include <uhd/exception.hpp>
+
+e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &)
+{
+ throw uhd::assertion_error("e300_fifo_interface::make() !E300_NATIVE");
+}
+
+#endif //E300_NATIVE
diff --git a/host/lib/usrp/e300/e300_fifo_config.hpp b/host/lib/usrp/e300/e300_fifo_config.hpp
new file mode 100644
index 000000000..967f451ca
--- /dev/null
+++ b/host/lib/usrp/e300/e300_fifo_config.hpp
@@ -0,0 +1,52 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_FIFO_CONFIG_HPP
+#define INCLUDED_E300_FIFO_CONFIG_HPP
+
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+struct e300_fifo_config_t
+{
+ size_t ctrl_length;
+ size_t buff_length;
+ size_t phys_addr;
+};
+
+e300_fifo_config_t e300_read_sysfs(void);
+std::string e300_get_sysfs_attr(const std::string &node, const std::string &attr);
+
+struct e300_fifo_interface : boost::enable_shared_from_this<e300_fifo_interface>
+{
+ typedef boost::shared_ptr<e300_fifo_interface> sptr;
+ static sptr make(const e300_fifo_config_t &config);
+
+ virtual uhd::transport::zero_copy_if::sptr make_recv_xport(
+ const size_t which_stream,
+ const uhd::transport::zero_copy_xport_params &params) = 0;
+
+ virtual uhd::transport::zero_copy_if::sptr make_send_xport(
+ const size_t which_stream,
+ const uhd::transport::zero_copy_xport_params &parms) = 0;
+
+ virtual size_t get_global_regs_base(void) const = 0;
+};
+
+#endif /* INCLUDED_E300_FIFO_CONFIG_HPP */
diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp
new file mode 100644
index 000000000..d58fd63a9
--- /dev/null
+++ b/host/lib/usrp/e300/e300_fpga_defs.hpp
@@ -0,0 +1,29 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_FPGA_DEFS_HPP
+#define INCLUDED_E300_FPGA_DEFS_HPP
+namespace uhd { namespace usrp { namespace e300 { namespace fpga {
+
+static const size_t NUM_RADIOS = 2;
+
+static const boost::uint32_t COMPAT_MAJOR = 4;
+static const boost::uint32_t COMPAT_MINOR = 0;
+
+}}}} // namespace
+
+#endif // INCLUDED_E300_FPGA_DEFS_HPP
diff --git a/host/lib/usrp/e300/e300_global_regs.cpp b/host/lib/usrp/e300/e300_global_regs.cpp
new file mode 100644
index 000000000..3ba895826
--- /dev/null
+++ b/host/lib/usrp/e300/e300_global_regs.cpp
@@ -0,0 +1,131 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_global_regs.hpp"
+
+#include <boost/cstdint.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <cstring>
+#include <iostream>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class global_regs_local_impl : public global_regs
+{
+public:
+ global_regs_local_impl(const size_t ctrl_base) : _ctrl_base(ctrl_base)
+ {
+ }
+
+ virtual ~global_regs_local_impl(void)
+ {
+ }
+
+ boost::uint32_t peek32(const uhd::wb_iface::wb_addr_type addr)
+ {
+ // setup readback register
+ _poke32(_ctrl_base + global_regs::SR_CORE_READBACK, addr);
+ return _peek32(_ctrl_base);
+ }
+
+ void poke32(const uhd::wb_iface::wb_addr_type addr, const boost::uint32_t data)
+ {
+ _poke32(_ctrl_base + static_cast<size_t>(addr), data);
+ }
+
+
+private:
+ const size_t _ctrl_base;
+
+ UHD_INLINE void _poke32(const boost::uint32_t addr, const boost::uint32_t data)
+ {
+ volatile boost::uint32_t *p = reinterpret_cast<boost::uint32_t *>(addr);
+ *p = data;
+ }
+
+ UHD_INLINE boost::uint32_t _peek32(const boost::uint32_t addr)
+ {
+ volatile const boost::uint32_t *p = reinterpret_cast<const boost::uint32_t *>(addr);
+ return *p;
+ }
+};
+
+global_regs::sptr global_regs::make(const size_t ctrl_base)
+{
+ return sptr(new global_regs_local_impl(ctrl_base));
+}
+
+class global_regs_zc_impl : public global_regs
+{
+public:
+ global_regs_zc_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport)
+ {
+ }
+
+ virtual ~global_regs_zc_impl(void)
+ {
+ }
+
+ boost::uint32_t peek32(const uhd::wb_iface::wb_addr_type addr)
+ {
+ global_regs_transaction_t transaction;
+ transaction.is_poke = uhd::htonx<boost::uint32_t>(0);
+ transaction.addr = uhd::htonx<boost::uint32_t>(
+ static_cast<boost::uint32_t>(addr));
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw std::runtime_error("global_regs_zc_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw std::runtime_error("global_regs_zc_impl recv timeout");
+ std::memcpy(&transaction, buff->cast<const void *>(), sizeof(transaction));
+ }
+ return uhd::ntohx<boost::uint32_t>(transaction.data);
+ }
+
+ void poke32(const uhd::wb_iface::wb_addr_type addr, const boost::uint32_t data)
+ {
+ global_regs_transaction_t transaction;
+ transaction.is_poke = uhd::htonx<boost::uint32_t>(1);
+ transaction.addr = uhd::htonx<boost::uint32_t>(
+ static_cast<boost::uint32_t>(addr));
+ transaction.data = uhd::htonx<boost::uint32_t>(data);
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("global_regs_zc_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ }
+
+private:
+ uhd::transport::zero_copy_if::sptr _xport;
+};
+
+global_regs::sptr global_regs::make(uhd::transport::zero_copy_if::sptr xport)
+{
+ return sptr(new global_regs_zc_impl(xport));
+}
+
+}}};
diff --git a/host/lib/usrp/e300/e300_global_regs.hpp b/host/lib/usrp/e300/e300_global_regs.hpp
new file mode 100644
index 000000000..12693da79
--- /dev/null
+++ b/host/lib/usrp/e300/e300_global_regs.hpp
@@ -0,0 +1,78 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_GLOBAL_REGS_HPP
+#define INCLUDED_E300_GLOBAL_REGS_HPP
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/transport/zero_copy.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+struct global_regs_transaction_t {
+ boost::uint32_t is_poke;
+ boost::uint32_t addr;
+ boost::uint32_t data;
+ boost::uint32_t pad;
+};
+
+class global_regs : boost::noncopyable, public virtual uhd::wb_iface
+{
+public:
+ typedef boost::shared_ptr<global_regs> sptr;
+
+ static sptr make(const size_t ctrl_base);
+ static sptr make(uhd::transport::zero_copy_if::sptr xport);
+
+ static const size_t SR_CORE_READBACK = 0;
+ static const size_t SR_CORE_MISC = 4;
+ static const size_t SR_CORE_TEST = 28;
+ static const size_t SR_CORE_XB_LOCAL = 32;
+
+ // leave some room for registers,
+ // xbar starts with an offset of one
+ // 1K page. A part of which is used for
+ // DST_LOOKUP for DST_LOOKUP
+
+ static const size_t SR_CORE_DST = 1024;
+ static const size_t SR_CORE_XBAR = 2048;
+
+ static const size_t RB32_CORE_MISC = 1;
+ static const size_t RB32_CORE_COMPAT = 2;
+ static const size_t RB32_CORE_GITHASH = 3;
+ static const size_t RB32_CORE_PLL = 4;
+ static const size_t RB32_CORE_TEST = 24;
+
+ // PPS selection
+ static const size_t PPS_GPS = 0;
+ static const size_t PPS_INT = 2;
+ static const size_t PPS_EXT = 3;
+};
+
+UHD_INLINE boost::uint32_t XB_ADDR(const boost::uint32_t addr)
+{
+ return global_regs::SR_CORE_XBAR + (addr << 2);
+}
+
+UHD_INLINE boost::uint32_t DST_ADDR(const boost::uint32_t addr)
+{
+ return global_regs::SR_CORE_DST + (addr << 2);
+}
+
+}}};
+
+#endif /* INCLUDED_E300_GLOBAL_REGS_HPP */
diff --git a/host/lib/usrp/e300/e300_i2c.cpp b/host/lib/usrp/e300/e300_i2c.cpp
new file mode 100644
index 000000000..d8f535a98
--- /dev/null
+++ b/host/lib/usrp/e300/e300_i2c.cpp
@@ -0,0 +1,409 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/transport/udp_simple.hpp>
+
+
+#include "e300_i2c.hpp"
+#include <cstring>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class zc_impl : public i2c
+{
+public:
+ zc_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport)
+ {
+ }
+
+ virtual ~zc_impl(void)
+ {
+ }
+
+ void set_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg,
+ const boost::uint8_t value)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = WRITE | ONEBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ transaction.data = value;
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("i2c_zc_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ }
+
+ boost::uint8_t get_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = READ | ONEBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw std::runtime_error("i2c_zc_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw std::runtime_error("i2c_zc_impl recv timeout");
+ std::memcpy(&transaction, buff->cast<const void *>(), sizeof(transaction));
+ }
+ return transaction.data;
+ }
+
+ void set_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg,
+ const boost::uint8_t value)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = WRITE | TWOBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ transaction.data = value;
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("i2c_zc_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ }
+
+ boost::uint8_t get_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = READ | TWOBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw std::runtime_error("i2c_zc_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &transaction, sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0);
+ if (not buff or buff->size() < sizeof(transaction))
+ throw std::runtime_error("i2c_zc_impl recv timeout");
+ std::memcpy(&transaction, buff->cast<const void *>(), sizeof(transaction));
+ }
+ return transaction.data;
+ }
+
+
+private:
+ uhd::transport::zero_copy_if::sptr _xport;
+};
+
+i2c::sptr i2c::make_zc(uhd::transport::zero_copy_if::sptr xport)
+{
+ return sptr(new zc_impl(xport));
+}
+
+class simple_udp_impl : public i2c
+{
+public:
+ simple_udp_impl(const std::string &ip_addr, const std::string &port)
+ {
+ _xport = uhd::transport::udp_simple::make_connected(ip_addr, port);
+ }
+
+ virtual ~simple_udp_impl(void)
+ {
+ }
+
+ void set_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg,
+ const boost::uint8_t value)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = i2c::WRITE | ONEBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ transaction.data = value;
+
+ _xport->send(
+ boost::asio::buffer(
+ &transaction,
+ sizeof(transaction)));
+ }
+
+ boost::uint8_t get_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = i2c::READ | ONEBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ transaction.data = 0;
+
+ _xport->send(
+ boost::asio::buffer(
+ &transaction,
+ sizeof(transaction)));
+
+ boost::uint8_t buff[sizeof(i2c_transaction_t)] = {};
+ const size_t nbytes = _xport->recv(
+ boost::asio::buffer(buff), 0.100);
+ if (not (nbytes == sizeof(transaction)))
+ throw std::runtime_error("i2c_simple_udp_impl recv timeout");
+ i2c_transaction_t *reply = reinterpret_cast<i2c_transaction_t*>(buff);
+ return reply->data;
+ }
+
+ void set_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg,
+ const boost::uint8_t value)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = i2c::WRITE | TWOBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ transaction.data = value;
+
+ _xport->send(
+ boost::asio::buffer(
+ &transaction,
+ sizeof(transaction)));
+ }
+
+ boost::uint8_t get_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg)
+ {
+ i2c_transaction_t transaction;
+ transaction.type = i2c::READ | TWOBYTE;
+ transaction.addr = addr;
+ transaction.reg = uhd::htonx<boost::uint16_t>(reg);
+ transaction.data = 0;
+
+ _xport->send(
+ boost::asio::buffer(
+ &transaction,
+ sizeof(transaction)));
+
+ boost::uint8_t buff[sizeof(i2c_transaction_t)] = {};
+ const size_t nbytes = _xport->recv(
+ boost::asio::buffer(buff), 0.100);
+ if (not (nbytes == sizeof(transaction)))
+ throw std::runtime_error("i2c_simple_udp_impl recv timeout");
+ i2c_transaction_t *reply = reinterpret_cast<i2c_transaction_t*>(buff);
+ return reply->data;
+ }
+
+private:
+ uhd::transport::udp_simple::sptr _xport;
+};
+
+i2c::sptr i2c::make_simple_udp(
+ const std::string &ip_addr,
+ const std::string &port)
+{
+ return sptr(new simple_udp_impl(ip_addr,port));
+}
+
+}}} // namespace
+
+#ifdef E300_NATIVE
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <boost/thread.hpp>
+#include <boost/cstdint.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class i2cdev_impl : public i2c
+{
+public:
+ i2cdev_impl(const std::string &device)
+ {
+ _fd = ::open(device.c_str(), O_RDWR);
+ if (_fd < 0)
+ throw uhd::system_error("open failed.");
+ }
+
+ virtual ~i2cdev_impl(void)
+ {
+ close(_fd);
+ }
+
+ void set_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg,
+ const boost::uint8_t value)
+ {
+ boost::uint8_t outbuf[2];
+ i2c_rdwr_ioctl_data packets;
+ i2c_msg messages[1];
+
+ messages[0].addr = addr;
+ messages[0].flags = 0;
+ messages[0].len = sizeof(outbuf);
+ messages[0].buf = outbuf;
+
+ outbuf[0] = reg;
+ outbuf[1] = value;
+
+ packets.msgs = messages;
+ packets.nmsgs = 1;
+
+ if(::ioctl(_fd, I2C_RDWR, &packets) < 0) {
+ throw std::runtime_error("ioctl failed");
+ }
+ // this is ugly
+ boost::this_thread::sleep(boost::posix_time::milliseconds(5));
+ }
+
+ boost::uint8_t get_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg)
+ {
+ i2c_rdwr_ioctl_data packets;
+ i2c_msg messages[2];
+
+ boost::uint8_t outbuf = reg;
+ messages[0].addr = addr;
+ messages[0].flags = 0;
+ messages[0].len = sizeof(outbuf);
+ messages[0].buf = &outbuf;
+
+ boost::uint8_t inbuf;
+ messages[1].addr = addr;
+ messages[1].flags = I2C_M_RD;
+ messages[1].len = sizeof(inbuf);
+ messages[1].buf = &inbuf;
+
+ packets.msgs = messages;
+ packets.nmsgs = 2;
+
+ if(::ioctl(_fd, I2C_RDWR, &packets) < 0) {
+ throw std::runtime_error("ioctl failed.");
+ }
+
+ return inbuf;
+ }
+
+ // the daughterboard uses 16 bit addresses
+ void set_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg,
+ const boost::uint8_t value)
+ {
+ boost::uint8_t outbuf[3];
+ i2c_rdwr_ioctl_data packets;
+ i2c_msg messages[1];
+
+ messages[0].addr = addr;
+ messages[0].flags = 0;
+ messages[0].len = sizeof(outbuf);
+ messages[0].buf = outbuf;
+
+ outbuf[0] = (reg >> 8) & 0xff;
+ outbuf[1] = reg & 0xff;
+ outbuf[2] = value;
+
+ packets.msgs = messages;
+ packets.nmsgs = 1;
+
+ if(::ioctl(_fd, I2C_RDWR, &packets) < 0) {
+ throw std::runtime_error("ioctl failed");
+ }
+ // this is ugly
+ boost::this_thread::sleep(boost::posix_time::milliseconds(5));
+ }
+
+
+ // the daughterboard eeprom uses 16 bit addresses
+ boost::uint8_t get_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg)
+ {
+ i2c_rdwr_ioctl_data packets;
+ i2c_msg messages[2];
+
+ // always little endian
+ boost::uint8_t outbuf[2];
+ outbuf[0] = (reg >> 8) & 0xff;
+ outbuf[1] = reg & 0xff;
+
+ messages[0].addr = addr;
+ messages[0].flags = 0;
+ messages[0].len = sizeof(outbuf);
+ messages[0].buf = outbuf;
+
+ boost::uint8_t inbuf;
+ messages[1].addr = addr;
+ messages[1].flags = I2C_M_RD;
+ messages[1].len = sizeof(inbuf);
+ messages[1].buf = &inbuf;
+
+ packets.msgs = messages;
+ packets.nmsgs = 2;
+
+ if(::ioctl(_fd, I2C_RDWR, &packets) < 0) {
+ throw std::runtime_error("ioctl failed.");
+ }
+
+ return inbuf;
+ }
+
+private:
+ int _fd;
+};
+
+}}} // namespace
+
+using namespace uhd::usrp::e300;
+
+i2c::sptr i2c::make_i2cdev(const std::string &device)
+{
+ return sptr(new i2cdev_impl(device));
+}
+#else
+using namespace uhd::usrp::e300;
+
+i2c::sptr i2c::make_i2cdev(const std::string &)
+{
+ throw uhd::assertion_error("i2c::make() !E300_NATIVE");
+}
+#endif // E300_NATIVE
diff --git a/host/lib/usrp/e300/e300_i2c.hpp b/host/lib/usrp/e300/e300_i2c.hpp
new file mode 100644
index 000000000..6cca7ab70
--- /dev/null
+++ b/host/lib/usrp/e300/e300_i2c.hpp
@@ -0,0 +1,77 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_I2C_HPP
+#define INCLUDED_E300_I2C_HPP
+
+#include <boost/noncopyable.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <uhd/transport/zero_copy.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+struct i2c_transaction_t {
+ boost::uint16_t reg;
+ boost::uint8_t addr;
+ boost::uint8_t data;
+ boost::uint8_t type;
+};
+
+class i2c : public boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<i2c> sptr;
+
+ static sptr make_i2cdev(const std::string &device);
+ static sptr make_zc(uhd::transport::zero_copy_if::sptr xport);
+ static sptr make_simple_udp(
+ const std::string &ip_addr,
+ const std::string &port);
+
+ virtual boost::uint8_t get_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg) = 0;
+
+ virtual boost::uint8_t get_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg) = 0;
+
+ virtual void set_i2c_reg8(
+ const boost::uint8_t addr,
+ const boost::uint8_t reg,
+ const boost::uint8_t value) = 0;
+
+ virtual void set_i2c_reg16(
+ const boost::uint8_t addr,
+ const boost::uint16_t reg,
+ const boost::uint8_t value) = 0;
+
+
+ static const boost::uint8_t DB_EEPROM_ADDR = 0x50;
+ static const boost::uint8_t MB_EEPROM_ADDR = 0x51;
+
+ static const boost::uint8_t WRITE = 0x1;
+ static const boost::uint8_t READ = 0x0;
+ static const boost::uint8_t TWOBYTE = 0x4;
+ static const boost::uint8_t ONEBYTE = 0x2;
+};
+
+}}};
+
+#endif // INCLUDED_E300_I2C_HPP
diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp
new file mode 100644
index 000000000..0f4cb5852
--- /dev/null
+++ b/host/lib/usrp/e300/e300_impl.cpp
@@ -0,0 +1,1347 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_impl.hpp"
+#include "e300_defaults.hpp"
+#include "e300_fpga_defs.hpp"
+#include "e300_spi.hpp"
+#include "e300_regs.hpp"
+#include "e300_eeprom_manager.hpp"
+#include "e300_sensor_manager.hpp"
+#include "e300_common.hpp"
+#include "e300_remote_codec_ctrl.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/sensors.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/bind.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <boost/asio.hpp>
+#include <fstream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace fs = boost::filesystem;
+namespace asio = boost::asio;
+
+//! mapping of frontend to radio perif index
+static const size_t FE0 = 1;
+static const size_t FE1 = 0;
+
+namespace uhd { namespace usrp { namespace e300 {
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+
+static std::vector<std::string> discover_ip_addrs(
+ const std::string& addr_hint, const std::string& port)
+{
+ std::vector<std::string> addrs;
+
+ // Create a UDP transport to communicate:
+ // Some devices will cause a throw when opened for a broadcast address.
+ // We print and recover so the caller can loop through all bcast addrs.
+ uhd::transport::udp_simple::sptr udp_bcast_xport;
+ try {
+ udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port);
+ } catch(const std::exception &e) {
+ UHD_MSG(error) << boost::format("Cannot open UDP transport on %s for discovery\n%s")
+ % addr_hint % e.what() << std::endl;
+ return addrs;
+ } catch(...) {
+ UHD_MSG(error) << "E300 Network discovery unknown error" << std::endl;
+ return addrs;
+ }
+
+ // TODO: Do not abuse the I2C transport here ...
+ // we send a read request to i2c address 0x51,
+ // to read register 0
+ i2c_transaction_t req;
+ req.type = i2c::READ | i2c::ONEBYTE;
+ req.addr = 0x51; // mboard's eeprom address, we don't really care
+ req.reg = 4;
+
+ // send dummy request
+ try {
+ udp_bcast_xport->send(boost::asio::buffer(&req, sizeof(req)));
+ } catch (const std::exception &ex) {
+ UHD_MSG(error) << "E300 Network discovery error " << ex.what() << std::endl;
+ return addrs;
+ } catch(...) {
+ UHD_MSG(error) << "E300 Network discovery unknown error" << std::endl;
+ return addrs;
+ }
+
+ // loop for replies until timeout
+ while (true) {
+ boost::uint8_t buff[sizeof(i2c_transaction_t)] = {};
+ const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050);
+ if (nbytes == 0)
+ break; //No more responses
+
+ const i2c_transaction_t *reply = reinterpret_cast<const i2c_transaction_t*>(buff);
+ if (req.addr == reply->addr)
+ addrs.push_back(udp_bcast_xport->get_recv_addr());
+ }
+
+ return addrs;
+}
+
+static bool is_loopback(const if_addrs_t &if_addrs)
+{
+ return if_addrs.inet == asio::ip::address_v4::loopback().to_string();
+}
+
+static device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
+{
+ // handle multi device discovery
+ device_addrs_t hints = separate_device_addr(multi_dev_hint);
+
+ if (hints.size() > 1) {
+ device_addrs_t found_devices;
+ std::string err_msg;
+ BOOST_FOREACH(const device_addr_t &hint_i, hints)
+ {
+ device_addrs_t found_devices_i = e300_find(hint_i);
+ if(found_devices_i.size() != 1)
+ err_msg += str(boost::format(
+ "Could not resolve device hint \"%s\" to a single device.")
+ % hint_i.to_string());
+ else
+ found_devices.push_back(found_devices_i[0]);
+ if (found_devices.empty())
+ return device_addrs_t();
+
+ if (not err_msg.empty())
+ throw uhd::value_error(err_msg);
+ }
+ return device_addrs_t(1, combine_device_addrs(found_devices));
+ }
+
+ // initialize the hint for a single device case
+ UHD_ASSERT_THROW(hints.size() <= 1);
+ hints.resize(1); // in case it was empty
+ device_addr_t hint = hints[0];
+ device_addrs_t e300_addrs;
+
+ // return an empty list of addresses when type is set to non-e300
+ if (hint.has_key("type") and hint["type"] != "e3x0")
+ return e300_addrs;
+
+ const bool loopback_only =
+ get_if_addrs().size() == 1 and is_loopback(get_if_addrs().at(0));
+
+ // if we don't have connectivity, we might as well skip the network part
+ if (not loopback_only) {
+ // if no address or node has been specified, send a broadcast
+ if ((not hint.has_key("addr")) and (not hint.has_key("node"))) {
+ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs())
+ {
+ // avoid the loopback device
+ if (is_loopback(if_addrs))
+ continue;
+
+ // create a new hint with this broadcast address
+ device_addr_t new_hint = hint;
+ new_hint["addr"] = if_addrs.bcast;
+
+ // call discover with the new hint ad append results
+ device_addrs_t new_e300_addrs = e300_find(new_hint);
+ e300_addrs.insert(e300_addrs.begin(),
+ new_e300_addrs.begin(), new_e300_addrs.end());
+
+ }
+ return e300_addrs;
+ }
+
+ std::vector<std::string> ip_addrs = discover_ip_addrs(
+ hint["addr"], E300_SERVER_I2C_PORT);
+
+ BOOST_FOREACH(const std::string &ip_addr, ip_addrs)
+ {
+ device_addr_t new_addr;
+ new_addr["type"] = "e3x0";
+ new_addr["addr"] = ip_addr;
+
+ // see if we can read the eeprom
+ try {
+ e300_eeprom_manager eeprom_manager(
+ i2c::make_simple_udp(new_addr["addr"], E300_SERVER_I2C_PORT));
+ const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom();
+ new_addr["name"] = eeprom["name"];
+ new_addr["serial"] = eeprom["serial"];
+ new_addr["product"] = eeprom["product"];
+ } catch (...) {
+ // set these values as empty string, so the device may still be found
+ // and the filters below can still operate on the discovered device
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+ // filter the discovered device below by matching optional keys
+ if ((not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]))
+ {
+ e300_addrs.push_back(new_addr);
+ }
+ }
+ }
+
+ // finally search locally
+ // if device node is not provided,
+ // use the default one
+ if (not hint.has_key("node")) {
+ device_addr_t new_addr = hint;
+ new_addr["node"] = "/dev/axi_fpga";
+ return e300_find(new_addr);
+ }
+
+ // use the given node
+ if (fs::exists(hint["node"])) {
+ device_addr_t new_addr;
+ new_addr["type"] = "e3x0";
+ new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string();
+
+ try {
+ e300_eeprom_manager eeprom_manager(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
+ const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom();
+ new_addr["name"] = eeprom["name"];
+ new_addr["serial"] = eeprom["serial"];
+ new_addr["product"] = eeprom["product"];
+ } catch (...) {
+ // set these values as empty string, so the device may still be found
+ // and the filters below can still operate on the discovered device
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+ // filter the discovered device below by matching optional keys
+ if ((not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]))
+ {
+ e300_addrs.push_back(new_addr);
+ }
+ }
+
+ return e300_addrs;
+}
+
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr e300_make(const device_addr_t &device_addr)
+{
+ UHD_LOG << "e300_make with args " << device_addr.to_pp_string() << std::endl;
+ if(device_addr.has_key("server"))
+ throw uhd::runtime_error(
+ str(boost::format("Please run the server executable \"%s\"")
+ % "usrp_e3x0_network_mode"));
+ else
+ return device::sptr(new e300_impl(device_addr));
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
+ : _device_addr(device_addr)
+ , _xport_path(device_addr.has_key("addr") ? ETH : AXI)
+ , _sid_framer(0)
+{
+ _type = uhd::device::USRP;
+
+ _async_md.reset(new async_md_type(1000/*messages deep*/));
+
+ ////////////////////////////////////////////////////////////////////
+ // load the fpga image
+ ////////////////////////////////////////////////////////////////////
+ if (_xport_path == AXI) {
+ if (not device_addr.has_key("no_reload_fpga")) {
+ // Load FPGA image if provided via args
+ if (device_addr.has_key("fpga")) {
+ common::load_fpga_image(device_addr["fpga"]);
+ // Else load the FPGA image based on the product ID
+ } else {
+ //extract the FPGA path for the e300
+ const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
+ device_addr["product"]);
+ std::string fpga_image;
+ switch(e300_eeprom_manager::get_mb_type(pid)) {
+ case e300_eeprom_manager::USRP_E310_MB:
+ fpga_image = find_image_path(E310_FPGA_FILE_NAME);
+ break;
+ case e300_eeprom_manager::USRP_E300_MB:
+ fpga_image = find_image_path(E300_FPGA_FILE_NAME);
+ break;
+ case e300_eeprom_manager::UNKNOWN:
+ default:
+ UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
+ << std::endl;
+ fpga_image = find_image_path(E300_FPGA_FILE_NAME);
+ break;
+ }
+ common::load_fpga_image(fpga_image);
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // setup fifo xports
+ ////////////////////////////////////////////////////////////////////
+ _ctrl_xport_params.recv_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE;
+ _ctrl_xport_params.num_recv_frames = e300::DEFAULT_CTRL_NUM_FRAMES;
+ _ctrl_xport_params.send_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE;
+ _ctrl_xport_params.num_send_frames = e300::DEFAULT_CTRL_NUM_FRAMES;
+
+ _data_xport_params.recv_frame_size = e300::DEFAULT_RX_DATA_FRAME_SIZE;
+ _data_xport_params.num_recv_frames = e300::DEFAULT_RX_DATA_NUM_FRAMES;
+ _data_xport_params.send_frame_size = e300::DEFAULT_TX_DATA_FRAME_SIZE;
+ _data_xport_params.num_send_frames = e300::DEFAULT_TX_DATA_NUM_FRAMES;
+
+ // until we figure out why this goes wrong we'll keep this hack around
+ if (_xport_path == ETH) {
+ _data_xport_params.recv_frame_size =
+ std::min(e300::MAX_NET_RX_DATA_FRAME_SIZE, _data_xport_params.recv_frame_size);
+ _data_xport_params.send_frame_size =
+ std::min(e300::MAX_NET_TX_DATA_FRAME_SIZE, _data_xport_params.send_frame_size);
+ }
+ udp_zero_copy::buff_params dummy_buff_params_out;
+
+ if (_xport_path == ETH) {
+ zero_copy_if::sptr codec_xport =
+ udp_zero_copy::make(device_addr["addr"], E300_SERVER_CODEC_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr);
+ _codec_ctrl = e300_remote_codec_ctrl::make(codec_xport);
+ zero_copy_if::sptr gregs_xport =
+ udp_zero_copy::make(device_addr["addr"], E300_SERVER_GREGS_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr);
+ _global_regs = global_regs::make(gregs_xport);
+
+ zero_copy_if::sptr i2c_xport;
+ i2c_xport = udp_zero_copy::make(device_addr["addr"], E300_SERVER_I2C_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr);
+ _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_zc(i2c_xport));
+
+ uhd::transport::zero_copy_xport_params sensor_xport_params;
+ sensor_xport_params.recv_frame_size = 128;
+ sensor_xport_params.num_recv_frames = 10;
+ sensor_xport_params.send_frame_size = 128;
+ sensor_xport_params.num_send_frames = 10;
+
+ zero_copy_if::sptr sensors_xport;
+ sensors_xport = udp_zero_copy::make(device_addr["addr"], E300_SERVER_SENSOR_PORT, sensor_xport_params, dummy_buff_params_out, device_addr);
+ _sensor_manager = e300_sensor_manager::make_proxy(sensors_xport);
+
+ } else {
+ e300_fifo_config_t fifo_cfg;
+ try {
+ fifo_cfg = e300_read_sysfs();
+ } catch (uhd::lookup_error &e) {
+ throw uhd::runtime_error("Failed to get driver parameters from sysfs.");
+ }
+ _fifo_iface = e300_fifo_interface::make(fifo_cfg);
+ _global_regs = global_regs::make(_fifo_iface->get_global_regs_base());
+
+ ad9361_params::sptr client_settings = boost::make_shared<e300_ad9361_client_t>();
+ _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1);
+ // This is horrible ... why do I have to sleep here?
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+ _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
+ }
+
+ UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush;
+ if (_xport_path == AXI) {
+ try {
+ _gps = gps::ublox::ubx::control::make("/dev/ttyPS1", 9600);
+ } catch (std::exception &e) {
+ UHD_MSG(error) << "An error occured making GPSDO control: " << e.what() << std::endl;
+ }
+ _sensor_manager = e300_sensor_manager::make_local(_gps);
+ }
+ UHD_MSG(status) << (_sensor_manager->get_gps_found() ? "found" : "not found") << std::endl;
+
+ // Verify we can talk to the e300 core control registers ...
+ UHD_MSG(status) << "Initializing core control..." << std::endl;
+ this->_register_loopback_self_test(_global_regs);
+
+ // Verify fpga compatibility version matches at least for the major
+ if (_get_version(FPGA_MAJOR) != fpga::COMPAT_MAJOR) {
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number %lu.x, but got %lu.%lu:\n"
+ "The FPGA build is not compatible with the host code build.\n"
+ "%s"
+ ) % fpga::COMPAT_MAJOR
+ % _get_version(FPGA_MAJOR) % _get_version(FPGA_MINOR)
+ % print_images_error()));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _tree = property_tree::make();
+ _tree->create<std::string>("/name").set("E-Series Device");
+ const fs_path mb_path = "/mboards/0";
+ _tree->create<std::string>(mb_path / "name")
+ .set(_eeprom_manager->get_mb_type_string());
+
+ _tree->create<std::string>(mb_path / "codename").set("Troll");
+
+ _tree->create<std::string>(mb_path / "fpga_version").set(
+ str(boost::format("%u.%u")
+ % _get_version(FPGA_MAJOR)
+ % _get_version(FPGA_MINOR)));
+
+ _tree->create<std::string>(mb_path / "fpga_version_hash").set(
+ _get_version_hash());
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<int>(mb_path / "sensors");
+ BOOST_FOREACH(const std::string &name, _sensor_manager->get_sensors())
+ {
+ _tree->create<sensor_value_t>(mb_path / "sensors" / name)
+ .publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name));
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(_eeprom_manager->get_mb_eeprom()) // set first...
+ .subscribe(boost::bind(
+ &e300_eeprom_manager::write_mb_eeprom,
+ _eeprom_manager, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // clocking
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<double>(mb_path / "tick_rate")
+ .coerce(boost::bind(&e300_impl::_set_tick_rate, this, _1))
+ .publish(boost::bind(&e300_impl::_get_tick_rate, this))
+ .subscribe(boost::bind(&e300_impl::_update_tick_rate, this, _1));
+
+ //default some chains on -- needed for setup purposes
+ _codec_ctrl->set_active_chains(true, false, true, false);
+ _codec_ctrl->set_clock_rate(50e6);
+
+ ////////////////////////////////////////////////////////////////////
+ // setup radios
+ ////////////////////////////////////////////////////////////////////
+ for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)
+ this->_setup_radio(instance);
+
+ _codec_ctrl->data_port_loopback(true);
+
+ // Radio 0 loopback through AD9361
+ this->_codec_loopback_self_test(_radio_perifs[0].ctrl);
+ // Radio 1 loopback through AD9361
+ this->_codec_loopback_self_test(_radio_perifs[1].ctrl);
+
+ _codec_ctrl->data_port_loopback(false);
+
+ ////////////////////////////////////////////////////////////////////
+ // internal gpios
+ ////////////////////////////////////////////////////////////////////
+ gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);
+ const std::vector<std::string> gpio_attrs = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX");
+ BOOST_FOREACH(const std::string &attr, gpio_attrs)
+ {
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr)
+ .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1))
+ .set(0);
+ }
+ _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK")
+ .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK"));
+
+
+ ////////////////////////////////////////////////////////////////////
+ // register the time keepers - only one can be the highlander
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<time_spec_t>(mb_path / "time" / "now")
+ .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64))
+ .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[0].time64, _1))
+ .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[1].time64, _1));
+ _tree->create<time_spec_t>(mb_path / "time" / "pps")
+ .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64))
+ .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[0].time64, _1))
+ .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[1].time64, _1));
+ //setup time source props
+ _tree->create<std::string>(mb_path / "time_source" / "value")
+ .subscribe(boost::bind(&e300_impl::_update_time_source, this, _1));
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources);
+ //setup reference source props
+ _tree->create<std::string>(mb_path / "clock_source" / "value")
+ .subscribe(boost::bind(&e300_impl::_update_clock_source, this, _1));
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal");
+ // not implemented ("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // dboard eeproms but not really
+ ////////////////////////////////////////////////////////////////////
+ dboard_eeprom_t db_eeprom;
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom")
+ .set(_eeprom_manager->get_db_eeprom())
+ .subscribe(boost::bind(
+ &e300_eeprom_manager::write_db_eeprom,
+ _eeprom_manager, _1));
+
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom")
+ .set(_eeprom_manager->get_db_eeprom())
+ .subscribe(boost::bind(
+ &e300_eeprom_manager::write_db_eeprom,
+ _eeprom_manager, _1));
+
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom);
+
+ ////////////////////////////////////////////////////////////////////
+ // create RF frontend interfacing
+ ////////////////////////////////////////////////////////////////////
+ {
+ const fs_path codec_path = mb_path / ("rx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set("E3x0 RX dual ADC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+ {
+ const fs_path codec_path = mb_path / ("tx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set("E3x0 TX dual DAC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create frontend mapping
+ ////////////////////////////////////////////////////////////////////
+
+ std::vector<size_t> default_map(2, 0);
+ default_map[0] = 0; // set A->0
+ default_map[1] = 1; // set B->1, even if there's only A
+
+ _tree->create<std::vector<size_t> >(mb_path / "rx_chan_dsp_mapping").set(default_map);
+ _tree->create<std::vector<size_t> >(mb_path / "tx_chan_dsp_mapping").set(default_map);
+
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .set(subdev_spec_t())
+ .subscribe(boost::bind(&e300_impl::_update_subdev_spec, this, "rx", _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .set(subdev_spec_t())
+ .subscribe(boost::bind(&e300_impl::_update_subdev_spec, this, "tx", _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // do some post-init tasks
+ ////////////////////////////////////////////////////////////////////
+
+ // init the clock rate to something reasonable
+ _tree->access<double>(mb_path / "tick_rate").set(
+ device_addr.cast<double>("master_clock_rate", e300::DEFAULT_TICK_RATE));
+
+ // subdev spec contains full width of selections
+ subdev_spec_t rx_spec, tx_spec;
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends"))
+ {
+ rx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends"))
+ {
+ tx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec);
+ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);
+
+ if (_sensor_manager->get_gps_found()) {
+ _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo");
+ _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo");
+ UHD_MSG(status) << "References initialized to GPSDO sources" << std::endl;
+ const time_t tp = time_t(_sensor_manager->get_gps_time().to_int());
+ _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
+ //wait for time to be set (timeout after 1 second)
+ for (int i = 0; i < 10; i++)
+ {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+ if(tp == (_tree->access<time_spec_t>(mb_path / "time" / "pps").get()).get_full_secs())
+ break;
+ }
+ } else {
+ // init to default time and clock source
+ _tree->access<std::string>(mb_path / "clock_source" / "value").set(
+ e300::DEFAULT_CLOCK_SRC);
+ _tree->access<std::string>(mb_path / "time_source" / "value").set(
+ e300::DEFAULT_TIME_SRC);
+
+ UHD_MSG(status) << "References initialized to internal sources" << std::endl;
+ }
+}
+
+boost::uint8_t e300_impl::_get_internal_gpio(
+ gpio_core_200::sptr gpio,
+ const std::string &)
+{
+ return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));
+}
+
+void e300_impl::_set_internal_gpio(
+ gpio_core_200::sptr gpio,
+ const std::string &attr,
+ const boost::uint32_t value)
+{
+ if (attr == "CTRL")
+ return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
+ else if (attr == "DDR")
+ return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
+ else if (attr == "OUT")
+ return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
+ else if (attr == "ATR_0X")
+ return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
+ else if (attr == "ATR_RX")
+ return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
+ else if (attr == "ATR_TX")
+ return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
+ else if (attr == "ATR_XX")
+ return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+}
+
+uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx)
+{
+ const boost::uint32_t st =
+ _global_regs->peek32(global_regs::RB32_CORE_PLL);
+ const bool locked = is_tx ? st & 0x1 : st & 0x2;
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+}
+
+e300_impl::~e300_impl(void)
+{
+ /* NOP */
+}
+
+void e300_impl::_enforce_tick_rate_limits(
+ const size_t chan_count,
+ const double tick_rate,
+ const std::string &direction)
+{
+ const size_t max_chans = 2;
+ if (chan_count > max_chans) {
+ throw uhd::value_error(boost::str(
+ boost::format("cannot not setup %d %s channels (maximum is %d)")
+ % chan_count
+ % direction
+ % max_chans
+ ));
+ } else {
+ const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE / ((chan_count <= 1) ? 1 : 2);
+ if (tick_rate - max_tick_rate >= 1.0)
+ {
+ throw uhd::value_error(boost::str(
+ boost::format("current master clock rate (%.6f MHz) exceeds maximum possible master clock rate (%.6f MHz) when using %d %s channels")
+ % (tick_rate/1e6)
+ % (max_tick_rate/1e6)
+ % chan_count
+ % direction
+ ));
+ }
+ }
+}
+
+double e300_impl::_set_tick_rate(const double rate)
+{
+ UHD_MSG(status) << "Asking for clock rate " << rate/1e6 << " MHz\n";
+ _tick_rate = _codec_ctrl->set_clock_rate(rate);
+ UHD_MSG(status) << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n";
+
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ perif.time64->set_tick_rate(_tick_rate);
+ perif.time64->self_test();
+ }
+ return _tick_rate;
+}
+
+void e300_impl::_load_fpga_image(const std::string &path)
+{
+ if (not fs::exists("/dev/xdevcfg"))
+ {
+ ::system("mknod /dev/xdevcfg c 259 0");
+ //throw uhd::runtime_error("no xdevcfg, please run: mknod /dev/xdevcfg c 259 0");
+ }
+
+ UHD_MSG(status) << "Loading FPGA image: " << path << "..." << std::flush;
+
+ std::ifstream fpga_file(path.c_str(), std::ios_base::binary);
+ UHD_ASSERT_THROW(fpga_file.good());
+
+ std::FILE *wfile;
+ wfile = std::fopen("/dev/xdevcfg", "wb");
+ UHD_ASSERT_THROW(!(wfile == NULL));
+
+ char buff[16384]; // devcfg driver can't handle huge writes
+ do {
+ fpga_file.read(buff, sizeof(buff));
+ std::fwrite(buff, 1, fpga_file.gcount(), wfile);
+ } while (fpga_file);
+
+ fpga_file.close();
+ std::fclose(wfile);
+
+ UHD_MSG(status) << " done" << std::endl;
+}
+
+void e300_impl::_register_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ UHD_MSG(status) << "Performing register loopback test... " << std::flush;
+ size_t hash = time(NULL);
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash));
+ test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash);
+ if (test_fail) break; //exit loop on any failure
+ }
+ UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
+}
+
+boost::uint32_t e300_impl::_get_version(compat_t which)
+{
+ const boost::uint16_t compat_num
+ = _global_regs->peek32(global_regs::RB32_CORE_COMPAT);
+
+ switch(which) {
+ case FPGA_MINOR:
+ return compat_num & 0xff;
+ case FPGA_MAJOR:
+ return (compat_num & 0xff00) >> 8;
+ default:
+ throw uhd::value_error("Requested unknown version.");
+ };
+}
+
+std::string e300_impl::_get_version_hash(void)
+{
+ const boost::uint32_t git_hash
+ = _global_regs->peek32(global_regs::RB32_CORE_GITHASH);
+ return str(boost::format("%7x%s")
+ % (git_hash & 0x0FFFFFFF)
+ % ((git_hash & 0xF000000) ? "-dirty" : ""));
+}
+
+void e300_impl::_codec_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ UHD_ASSERT_THROW(bool(iface));
+ UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
+ size_t hash = time(NULL);
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
+ iface->poke32(TOREG(SR_CODEC_IDLE), word32);
+ iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate
+ const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK);
+ const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
+ const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
+ test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail) break; //exit loop on any failure
+ }
+ UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
+
+ /* Zero out the idle data. */
+ iface->poke32(TOREG(SR_CODEC_IDLE), 0);
+}
+
+boost::uint32_t e300_impl::_allocate_sid(const sid_config_t &config)
+{
+ const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff;
+
+ const boost::uint32_t sid = 0
+ | (E300_DEVICE_HERE << 24)
+ | (_sid_framer << 16)
+ | (config.router_addr_there << 8)
+ | (stream << 0)
+ ;
+ UHD_LOG << std::hex
+ << " sid 0x" << sid
+ << " framer 0x" << _sid_framer
+ << " stream 0x" << stream
+ << " router_dst_there 0x" << int(config.router_dst_there)
+ << " router_addr_there 0x" << int(config.router_addr_there)
+ << std::dec << std::endl;
+
+ // Program the E300 to recognize it's own local address.
+ _global_regs->poke32(global_regs::SR_CORE_XB_LOCAL, config.router_addr_there);
+
+ // Program CAM entry for outgoing packets matching a E300 resource (e.g. Radio).
+ // This type of packet matches the XB_LOCAL address and is looked up in the upper
+ // half of the CAM
+ _global_regs->poke32(XB_ADDR(256 + stream),
+ config.router_dst_there);
+
+ // Program CAM entry for returning packets to us (for example GR host via zynq_fifo)
+ // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM
+ _global_regs->poke32(XB_ADDR(E300_DEVICE_HERE),
+ config.router_dst_here);
+
+ UHD_LOG << std::hex
+ << "done router config for sid 0x" << sid
+ << std::dec << std::endl;
+
+ //increment for next setup
+ _sid_framer++;
+
+ return sid;
+}
+
+void e300_impl::_setup_dest_mapping(const boost::uint32_t sid, const size_t which_stream)
+{
+ UHD_LOG << boost::format("Setting up dest map for 0x%lx to be stream %d")
+ % (sid & 0xff) % which_stream << std::endl;
+ _global_regs->poke32(DST_ADDR(sid & 0xff), which_stream);
+}
+
+void e300_impl::_update_time_source(const std::string &source)
+{
+ UHD_MSG(status) << boost::format("Setting time source to %s") % source << std::endl;
+ if (source == "none" or source == "internal") {
+ _misc.pps_sel = global_regs::PPS_INT;
+ } else if (source == "gpsdo") {
+ _misc.pps_sel = global_regs::PPS_GPS;
+ } else if (source == "external") {
+ _misc.pps_sel = global_regs::PPS_EXT;
+ } else {
+ throw uhd::key_error("update_time_source: unknown source: " + source);
+ }
+ _update_gpio_state();
+}
+
+size_t e300_impl::_get_axi_dma_channel(
+ boost::uint8_t destination,
+ boost::uint8_t prefix)
+{
+ static const boost::uint32_t RADIO_GRP_SIZE = 4;
+ static const boost::uint32_t RADIO0_GRP = 0;
+ static const boost::uint32_t RADIO1_GRP = 1;
+
+ boost::uint32_t radio_grp = (destination == E300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP;
+ return ((radio_grp * RADIO_GRP_SIZE) + prefix);
+}
+
+boost::uint16_t e300_impl::_get_udp_port(
+ boost::uint8_t destination,
+ boost::uint8_t prefix)
+{
+ if (destination == E300_XB_DST_R0) {
+ if (prefix == E300_RADIO_DEST_PREFIX_CTRL)
+ return boost::lexical_cast<boost::uint16_t>(E300_SERVER_CTRL_PORT0);
+ else if (prefix == E300_RADIO_DEST_PREFIX_TX)
+ return boost::lexical_cast<boost::uint16_t>(E300_SERVER_TX_PORT0);
+ else if (prefix == E300_RADIO_DEST_PREFIX_RX)
+ return boost::lexical_cast<boost::uint16_t>(E300_SERVER_RX_PORT0);
+ } else if (destination == E300_XB_DST_R1) {
+ if (prefix == E300_RADIO_DEST_PREFIX_CTRL)
+ return boost::lexical_cast<boost::uint16_t>(E300_SERVER_CTRL_PORT1);
+ else if (prefix == E300_RADIO_DEST_PREFIX_TX)
+ return boost::lexical_cast<boost::uint16_t>(E300_SERVER_TX_PORT1);
+ else if (prefix == E300_RADIO_DEST_PREFIX_RX)
+ return boost::lexical_cast<boost::uint16_t>(E300_SERVER_RX_PORT1);
+ }
+ throw uhd::value_error(str(boost::format("No UDP port defined for combination: %u %u") % destination % prefix));
+}
+
+e300_impl::both_xports_t e300_impl::_make_transport(
+ const boost::uint8_t &destination,
+ const boost::uint8_t &prefix,
+ const uhd::transport::zero_copy_xport_params &params,
+ boost::uint32_t &sid)
+{
+ both_xports_t xports;
+
+ sid_config_t config;
+ config.router_addr_there = E300_DEVICE_THERE;
+ config.dst_prefix = prefix;
+ config.router_dst_there = destination;
+ config.router_dst_here = E300_XB_DST_AXI;
+ sid = this->_allocate_sid(config);
+
+ // in local mode
+ if (_xport_path == AXI) {
+ // lookup which dma channel we need
+ // to use to create our transport
+ const size_t stream = _get_axi_dma_channel(
+ destination,
+ prefix);
+
+ xports.send =
+ _fifo_iface->make_send_xport(stream, params);
+ xports.recv =
+ _fifo_iface->make_recv_xport(stream, params);
+
+ // in network mode
+ } else if (_xport_path == ETH) {
+ // lookup which udp port we need
+ // to use to create our transport
+ const boost::uint16_t port = _get_udp_port(
+ destination,
+ prefix);
+
+ udp_zero_copy::buff_params dummy_buff_params_out;
+ xports.send = udp_zero_copy::make(
+ _device_addr["addr"],
+ str(boost::format("%u") % port), params,
+ dummy_buff_params_out,
+ _device_addr);
+
+ // use the same xport in both directions
+ xports.recv = xports.send;
+ }
+
+ // configure the return path
+ _setup_dest_mapping(sid, _get_axi_dma_channel(destination, prefix));
+
+ return xports;
+}
+
+void e300_impl::_update_clock_source(const std::string &)
+{
+}
+
+void e300_impl::_update_antenna_sel(const size_t &which, const std::string &ant)
+{
+ if (ant != "TX/RX" and ant != "RX2")
+ throw uhd::value_error("e300: unknown RX antenna option: " + ant);
+ _radio_perifs[which].ant_rx2 = (ant == "RX2");
+ this->_update_atrs();
+}
+
+void e300_impl::_update_fe_lo_freq(const std::string &fe, const double freq)
+{
+ if (fe[0] == 'R')
+ _settings.rx_freq = freq;
+ if (fe[0] == 'T')
+ _settings.tx_freq = freq;
+ this->_update_atrs();
+ _update_bandsel(fe, freq);
+}
+
+void e300_impl::_setup_radio(const size_t dspno)
+{
+ radio_perifs_t &perif = _radio_perifs[dspno];
+ const fs_path mb_path = "/mboards/0";
+
+ ////////////////////////////////////////////////////////////////////
+ // crossbar config for ctrl xports
+ ////////////////////////////////////////////////////////////////////
+
+ // make a transport, grab a sid
+ boost::uint32_t ctrl_sid;
+ both_xports_t ctrl_xports = _make_transport(
+ dspno ? E300_XB_DST_R1 : E300_XB_DST_R0,
+ E300_RADIO_DEST_PREFIX_CTRL,
+ _ctrl_xport_params,
+ ctrl_sid);
+
+ this->_setup_dest_mapping(
+ ctrl_sid,
+ dspno ? E300_R1_CTRL_STREAM
+ : E300_R0_CTRL_STREAM);
+
+ ////////////////////////////////////////////////////////////////////
+ // radio control
+ ////////////////////////////////////////////////////////////////////
+ perif.ctrl = radio_ctrl_core_3000::make(
+ false/*lilE*/,
+ ctrl_xports.send,
+ ctrl_xports.recv,
+ ctrl_sid,
+ dspno ? "1" : "0");
+ this->_register_loopback_self_test(perif.ctrl);
+ perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO));
+
+ ////////////////////////////////////////////////////////////////////
+ // front end corrections
+ ////////////////////////////////////////////////////////////////////
+ std::string slot_name = (dspno == 0) ? "A" : "B";
+ perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT));
+ const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name;
+ _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1))
+ .set(true);
+ _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+
+ perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT));
+ const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name;
+ _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value")
+ .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+ _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value")
+ .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1))
+ .set(std::complex<double>(0.0, 0.0));
+
+ ////////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
+ perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP));
+ perif.ddc->set_link_rate(10e9/8); //whatever
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
+ .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
+ const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range")
+ .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc));
+ _tree->create<double>(rx_dsp_path / "rate" / "value")
+ .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1))
+ .subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1))
+ .set(e300::DEFAULT_RX_SAMP_RATE);
+ _tree->create<double>(rx_dsp_path / "freq" / "value")
+ .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
+ .set(e300::DEFAULT_DDC_FREQ);
+ _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
+ .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // create tx dsp control objects
+ ////////////////////////////////////////////////////////////////////
+ perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
+ perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
+ perif.duc->set_link_rate(10e9/8); //whatever
+ _tree->access<double>(mb_path / "tick_rate")
+ .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))
+ .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
+ const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno);
+ _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range")
+ .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc));
+ _tree->create<double>(tx_dsp_path / "rate" / "value")
+ .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1))
+ .subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1))
+ .set(e300::DEFAULT_TX_SAMP_RATE);
+ _tree->create<double>(tx_dsp_path / "freq" / "value")
+ .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
+ .set(e300::DEFAULT_DUC_FREQ);
+ _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
+ .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time_core_3000::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_now = RB64_TIME_NOW;
+ time64_rb_bases.rb_pps = RB64_TIME_PPS;
+ perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+
+ ////////////////////////////////////////////////////////////////////
+ // create RF frontend interfacing
+ ////////////////////////////////////////////////////////////////////
+ static const std::vector<std::string> data_directions = boost::assign::list_of("rx")("tx");
+ BOOST_FOREACH(const std::string& direction, data_directions)
+ {
+ const std::string key = boost::to_upper_copy(direction) + std::string(((dspno == FE0)? "1" : "2"));
+ const fs_path rf_fe_path
+ = mb_path / "dboards" / "A" / (direction + "_frontends") / ((dspno == 0) ? "A" : "B");
+
+ _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key);
+ _tree->create<int>(rf_fe_path / "sensors"); //empty TODO
+ _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
+ .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx"));
+ BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
+ {
+ _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range")
+ .set(ad9361_ctrl::get_gain_range(key));
+
+ _tree->create<double>(rf_fe_path / "gains" / name / "value")
+ .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
+ .set(e300::DEFAULT_FE_GAIN);
+ }
+ _tree->create<std::string>(rf_fe_path / "connection").set("IQ");
+ _tree->create<bool>(rf_fe_path / "enabled").set(true);
+ _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false);
+ _tree->create<double>(rf_fe_path / "bandwidth" / "value")
+ .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
+ .set(e300::DEFAULT_FE_BW);
+ _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range")
+ .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key));
+ _tree->create<double>(rf_fe_path / "freq" / "value")
+ .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1))
+ .set(e300::DEFAULT_FE_FREQ);
+ _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
+ .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
+
+ //setup antenna stuff
+ if (key[0] == 'R') {
+ static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
+ _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
+ _tree->create<std::string>(rf_fe_path / "antenna" / "value")
+ .subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1))
+ .set("RX2");
+
+ }
+ if (key[0] == 'T') {
+ static const std::vector<std::string> ants(1, "TX/RX");
+ _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
+ _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX");
+ }
+ }
+}
+
+void e300_impl::_update_enables(void)
+{
+ //extract settings from state variables
+ const bool enb_tx1 = bool(_radio_perifs[FE0].tx_streamer.lock());
+ const bool enb_rx1 = bool(_radio_perifs[FE0].rx_streamer.lock());
+ const bool enb_tx2 = bool(_radio_perifs[FE1].tx_streamer.lock());
+ const bool enb_rx2 = bool(_radio_perifs[FE1].rx_streamer.lock());
+ const size_t num_rx = (enb_rx1 ? 1 : 0) + (enb_rx2 ? 1:0);
+ const size_t num_tx = (enb_tx1 ? 1 : 0) + (enb_tx2 ? 1:0);
+ const bool mimo = num_rx == 2 or num_tx == 2;
+
+ //setup the active chains in the codec
+ _codec_ctrl->set_active_chains(enb_tx1, enb_tx2, enb_rx1, enb_rx2);
+ if ((num_rx + num_tx) == 0)
+ _codec_ctrl->set_active_chains(
+ true, false, true, false); // enable something
+
+ //set_active_chains could cause a clock rate change - reset dcm
+ _reset_codec_mmcm();
+
+ //figure out if mimo is enabled based on new state
+ _misc.mimo = (mimo)? 1 : 0;
+ _update_gpio_state();
+
+ //atrs change based on enables
+ _update_atrs();
+}
+
+void e300_impl::_update_gpio_state(void)
+{
+ boost::uint32_t misc_reg = 0
+ | (_misc.pps_sel << gpio_t::PPS_SEL)
+ | (_misc.mimo << gpio_t::MIMO)
+ | (_misc.codec_arst << gpio_t::CODEC_ARST)
+ | (_misc.tx_bandsels << gpio_t::TX_BANDSEL)
+ | (_misc.rx_bandsel_a << gpio_t::RX_BANDSELA)
+ | (_misc.rx_bandsel_b << gpio_t::RX_BANDSELB)
+ | (_misc.rx_bandsel_c << gpio_t::RX_BANDSELC);
+ _global_regs->poke32(global_regs::SR_CORE_MISC, misc_reg);
+}
+
+void e300_impl::_reset_codec_mmcm(void)
+{
+ _misc.codec_arst = 1;
+ _update_gpio_state();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _misc.codec_arst = 0;
+ _update_gpio_state();
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//////////////// ATR SETUP FOR FRONTEND CONTROL VIA GPIO ///////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+void e300_impl::_update_bandsel(const std::string& which, double freq)
+{
+ if(which[0] == 'R') {
+ if (freq < 450e6) {
+ _misc.rx_bandsel_a = 44; // 4 | (5 << 3)
+ _misc.rx_bandsel_b = 0; // 0 | (0 << 2)
+ _misc.rx_bandsel_c = 6; // 2 | (1 << 2)
+ } else if (freq < 700e6) {
+ _misc.rx_bandsel_a = 26; // 2 | (3 << 3)
+ _misc.rx_bandsel_b = 0; // 0 | (0 << 2)
+ _misc.rx_bandsel_c = 15; // 3 | (3 << 2)
+ } else if (freq < 1200e6) {
+ _misc.rx_bandsel_a = 8; // 0 | (1 << 3)
+ _misc.rx_bandsel_b = 0; // 0 | (0 << 2)
+ _misc.rx_bandsel_c = 9; // 1 | (2 << 2)
+ } else if (freq < 1800e6) {
+ _misc.rx_bandsel_a = 1; // 1 | (0 << 3)
+ _misc.rx_bandsel_b = 6; // 2 | (1 << 2)
+ _misc.rx_bandsel_c = 0; // 0 | (0 << 2)
+ } else if (freq < 2350e6){
+ _misc.rx_bandsel_a = 19; // 3 | (2 << 3)
+ _misc.rx_bandsel_b = 15; // 3 | (3 << 2)
+ _misc.rx_bandsel_c = 0; // 0 | (0 << 2)
+ } else if (freq < 2600e6){
+ _misc.rx_bandsel_a = 37; // 5 | (4 << 3)
+ _misc.rx_bandsel_b = 9; // 1 | (2 << 2)
+ _misc.rx_bandsel_c = 0; // 0 | (0 << 2)
+ } else {
+ _misc.rx_bandsel_a = 0;
+ _misc.rx_bandsel_b = 0;
+ _misc.rx_bandsel_c = 0;
+ }
+ _update_gpio_state();
+ } else if(which[0] == 'T') {
+ if (freq < 117.7e6)
+ _misc.tx_bandsels = 7;
+ else if (freq < 178.2e6)
+ _misc.tx_bandsels = 6;
+ else if (freq < 284.3e6)
+ _misc.tx_bandsels = 5;
+ else if (freq < 453.7e6)
+ _misc.tx_bandsels = 4;
+ else if (freq < 723.8e6)
+ _misc.tx_bandsels = 3;
+ else if (freq < 1154.9e6)
+ _misc.tx_bandsels = 2;
+ else if (freq < 1842.6e6)
+ _misc.tx_bandsels = 1;
+ else if (freq < 2940.0e6)
+ _misc.tx_bandsels = 0;
+ else
+ _misc.tx_bandsels = 7;
+ _update_gpio_state();
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+
+void e300_impl::_update_atrs(void)
+{
+ for (size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)
+ {
+ // if we're not ready, no point ...
+ if (not _radio_perifs[instance].atr)
+ return;
+
+ radio_perifs_t &perif = _radio_perifs[instance];
+ const bool enb_rx = bool(perif.rx_streamer.lock());
+ const bool enb_tx = bool(perif.tx_streamer.lock());
+ const bool rx_ant_rx2 = perif.ant_rx2;
+
+ const bool rx_low_band = _settings.rx_freq < 2.6e9;
+ const bool tx_low_band = _settings.tx_freq < 2940.0e6;
+
+ // VCRX
+ int vcrx_v1_rxing = 1;
+ int vcrx_v2_rxing = 0;
+ int vcrx_v1_txing = 1;
+ int vcrx_v2_txing = 0;
+
+ if (rx_low_band) {
+ vcrx_v1_rxing = rx_ant_rx2 ? 0 : 1;
+ vcrx_v2_rxing = rx_ant_rx2 ? 1 : 0;
+ vcrx_v1_txing = 0;
+ vcrx_v2_txing = 1;
+ } else {
+ vcrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
+ vcrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
+ vcrx_v1_txing = 1;
+ vcrx_v2_txing = 0;
+ }
+
+ // VCTX
+ int vctxrx_v1_rxing = 0;
+ int vctxrx_v2_rxing = 1;
+ int vctxrx_v1_txing = 0;
+ int vctxrx_v2_txing = 1;
+
+ if (tx_low_band) {
+ vctxrx_v1_rxing = rx_ant_rx2 ? 0 : 0;
+ vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
+ vctxrx_v1_txing = 1;
+ vctxrx_v2_txing = 0;
+ } else {
+ vctxrx_v1_rxing = rx_ant_rx2 ? 0 : 0;
+ vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
+ vctxrx_v1_txing = 1;
+ vctxrx_v2_txing = 1;
+ }
+ //swapped for routing reasons, reswap it here
+ if (instance == 1) {
+ std::swap(vctxrx_v1_rxing, vctxrx_v2_rxing);
+ std::swap(vctxrx_v1_txing, vctxrx_v2_txing);
+ }
+
+ int tx_enable_a = (!tx_low_band and enb_tx) ? 1 : 0;
+ int tx_enable_b = (tx_low_band and enb_tx) ? 1 : 0;
+
+ //----------------- LEDS ----------------------------//
+ const int led_rx2 = rx_ant_rx2 ? 1 : 0;
+ const int led_txrx = !rx_ant_rx2 ? 1 : 0;
+ const int led_tx = 1;
+
+ const int rx_leds = (led_rx2 << LED_RX_RX) | (led_txrx << LED_TXRX_RX);
+ const int tx_leds = (led_tx << LED_TXRX_TX);
+ const int xx_leds = tx_leds | (1 << LED_RX_RX); //forced to rx2
+
+ const int rx_selects = 0
+ | (vcrx_v1_rxing << VCRX_V1)
+ | (vcrx_v2_rxing << VCRX_V2)
+ | (vctxrx_v1_rxing << VCTXRX_V1)
+ | (vctxrx_v2_rxing << VCTXRX_V2)
+ ;
+ const int tx_selects = 0
+ | (vcrx_v1_txing << VCRX_V1)
+ | (vcrx_v2_txing << VCRX_V2)
+ | (vctxrx_v1_txing << VCTXRX_V1)
+ | (vctxrx_v2_txing << VCTXRX_V2)
+ ;
+ const int tx_enables = 0
+ | (tx_enable_a << TX_ENABLEA)
+ | (tx_enable_b << TX_ENABLEB)
+ ;
+
+ //default selects
+ int oo_reg = rx_selects;
+ int rx_reg = rx_selects;
+ int tx_reg = tx_selects;
+ int fd_reg = tx_selects; //tx selects dominate in fd mode
+
+ //add in leds and tx enables based on fe enable
+ if (enb_rx)
+ rx_reg |= rx_leds;
+ if (enb_rx)
+ fd_reg |= xx_leds;
+ if (enb_tx)
+ tx_reg |= tx_enables | tx_leds;
+ if (enb_tx)
+ fd_reg |= tx_enables | xx_leds;
+
+ gpio_core_200_32wo::sptr atr = _radio_perifs[instance].atr;
+ atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, oo_reg);
+ atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rx_reg);
+ atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_reg);
+ atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd_reg);
+ }
+}
+
+}}} // namespace
+
+UHD_STATIC_BLOCK(register_e300_device)
+{
+ device::register_device(&uhd::usrp::e300::e300_find, &uhd::usrp::e300::e300_make, uhd::device::USRP);
+}
diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp
new file mode 100644
index 000000000..9cfd80afd
--- /dev/null
+++ b/host/lib/usrp/e300/e300_impl.hpp
@@ -0,0 +1,296 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_IMPL_HPP
+#define INCLUDED_E300_IMPL_HPP
+
+#include <uhd/device.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/types/sensors.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+#include "e300_fifo_config.hpp"
+#include "radio_ctrl_core_3000.hpp"
+#include "rx_frontend_core_200.hpp"
+#include "tx_frontend_core_200.hpp"
+#include "rx_vita_core_3000.hpp"
+#include "tx_vita_core_3000.hpp"
+#include "time_core_3000.hpp"
+#include "rx_dsp_core_3000.hpp"
+#include "tx_dsp_core_3000.hpp"
+#include "ad9361_ctrl.hpp"
+#include "gpio_core_200.hpp"
+
+#include "e300_global_regs.hpp"
+#include "e300_i2c.hpp"
+#include "e300_eeprom_manager.hpp"
+#include "e300_sensor_manager.hpp"
+#include "e300_ublox_control.hpp"
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit";
+static const std::string E310_FPGA_FILE_NAME = "usrp_e310_fpga.bit";
+
+static const std::string E300_TEMP_SYSFS = "iio:device0";
+static const std::string E300_SPIDEV_DEVICE = "/dev/spidev0.1";
+static const std::string E300_I2CDEV_DEVICE = "/dev/i2c-0";
+
+static std::string E300_SERVER_RX_PORT0 = "21756";
+static std::string E300_SERVER_TX_PORT0 = "21757";
+static std::string E300_SERVER_CTRL_PORT0 = "21758";
+
+static std::string E300_SERVER_RX_PORT1 = "21856";
+static std::string E300_SERVER_TX_PORT1 = "21857";
+static std::string E300_SERVER_CTRL_PORT1 = "21858";
+
+
+static std::string E300_SERVER_CODEC_PORT = "21759";
+static std::string E300_SERVER_GREGS_PORT = "21760";
+static std::string E300_SERVER_I2C_PORT = "21761";
+static std::string E300_SERVER_SENSOR_PORT = "21762";
+
+static const double E300_RX_SW_BUFF_FULLNESS = 0.9; //Buffer should be half full
+
+// crossbar settings
+static const boost::uint8_t E300_RADIO_DEST_PREFIX_TX = 0;
+static const boost::uint8_t E300_RADIO_DEST_PREFIX_CTRL = 1;
+static const boost::uint8_t E300_RADIO_DEST_PREFIX_RX = 2;
+
+static const boost::uint8_t E300_XB_DST_AXI = 0;
+static const boost::uint8_t E300_XB_DST_R0 = 1;
+static const boost::uint8_t E300_XB_DST_R1 = 2;
+static const boost::uint8_t E300_XB_DST_CE0 = 3;
+static const boost::uint8_t E300_XB_DST_CE1 = 4;
+
+static const boost::uint8_t E300_DEVICE_THERE = 2;
+static const boost::uint8_t E300_DEVICE_HERE = 0;
+
+static const size_t E300_R0_CTRL_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_CTRL;
+static const size_t E300_R0_TX_DATA_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_TX;
+static const size_t E300_R0_RX_DATA_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_RX;
+
+static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_CTRL;
+static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX;
+static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX;
+
+
+/*!
+ * USRP-E300 implementation guts:
+ * The implementation details are encapsulated here.
+ * Handles properties on the mboard, dboard, dsps...
+ */
+class e300_impl : public uhd::device
+{
+public:
+ //structors
+ e300_impl(const uhd::device_addr_t &);
+ virtual ~e300_impl(void);
+
+ //the io interface
+ boost::mutex _stream_spawn_mutex;
+ uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &);
+ uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &);
+
+ typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
+ boost::shared_ptr<async_md_type> _async_md;
+
+ bool recv_async_msg(uhd::async_metadata_t &, double);
+
+private: // types
+ // sid convenience struct
+ struct sid_config_t
+ {
+ boost::uint8_t router_addr_there;
+ boost::uint8_t dst_prefix; //2bits
+ boost::uint8_t router_dst_there;
+ boost::uint8_t router_dst_here;
+ };
+
+ // perifs in the radio core
+ struct radio_perifs_t
+ {
+ radio_ctrl_core_3000::sptr ctrl;
+ gpio_core_200_32wo::sptr atr;
+ time_core_3000::sptr time64;
+ rx_vita_core_3000::sptr framer;
+ rx_dsp_core_3000::sptr ddc;
+ tx_vita_core_3000::sptr deframer;
+ tx_dsp_core_3000::sptr duc;
+ rx_frontend_core_200::sptr rx_fe;
+ tx_frontend_core_200::sptr tx_fe;
+
+ boost::weak_ptr<uhd::rx_streamer> rx_streamer;
+ boost::weak_ptr<uhd::tx_streamer> tx_streamer;
+
+ bool ant_rx2;
+ };
+
+ //frontend cache so we can update gpios
+ struct fe_control_settings_t
+ {
+ fe_control_settings_t(void)
+ {
+ rx_freq = 1e9;
+ tx_freq = 1e9;
+ }
+ double rx_freq;
+ double tx_freq;
+ };
+
+ // convenience struct
+ struct both_xports_t
+ {
+ uhd::transport::zero_copy_if::sptr recv;
+ uhd::transport::zero_copy_if::sptr send;
+ };
+
+ enum xport_t {AXI, ETH};
+
+ enum compat_t {FPGA_MAJOR, FPGA_MINOR};
+
+ struct gpio_t
+ {
+ gpio_t() : pps_sel(global_regs::PPS_INT),
+ mimo(0), codec_arst(0), tx_bandsels(0),
+ rx_bandsel_a(0), rx_bandsel_b(0), rx_bandsel_c(0)
+ {}
+
+ boost::uint32_t pps_sel;
+ boost::uint32_t mimo;
+ boost::uint32_t codec_arst;
+
+ boost::uint32_t tx_bandsels;
+ boost::uint32_t rx_bandsel_a;
+ boost::uint32_t rx_bandsel_b;
+ boost::uint32_t rx_bandsel_c;
+
+ static const size_t PPS_SEL = 0;
+ static const size_t MIMO = 2;
+ static const size_t CODEC_ARST = 3;
+ static const size_t TX_BANDSEL = 4;
+ static const size_t RX_BANDSELA = 7;
+ static const size_t RX_BANDSELB = 13;
+ static const size_t RX_BANDSELC = 17;
+ };
+
+private: // methods
+ void _load_fpga_image(const std::string &path);
+
+ void _register_loopback_self_test(uhd::wb_iface::sptr iface);
+
+ boost::uint32_t _get_version(compat_t which);
+ std::string _get_version_hash(void);
+
+ void _setup_radio(const size_t which_radio);
+
+ boost::uint32_t _allocate_sid(const sid_config_t &config);
+
+ void _setup_dest_mapping(
+ const boost::uint32_t sid,
+ const size_t which_stream);
+
+ size_t _get_axi_dma_channel(
+ boost::uint8_t destination,
+ boost::uint8_t prefix);
+
+ boost::uint16_t _get_udp_port(
+ boost::uint8_t destination,
+ boost::uint8_t prefix);
+
+ both_xports_t _make_transport(
+ const boost::uint8_t &destination,
+ const boost::uint8_t &prefix,
+ const uhd::transport::zero_copy_xport_params &params,
+ boost::uint32_t &sid);
+
+ double _get_tick_rate(void){return _tick_rate;}
+ double _set_tick_rate(const double rate);
+
+ void _update_gpio_state(void);
+ void _update_enables(void);
+ void _reset_codec_mmcm(void);
+ void _update_bandsel(const std::string& which, double freq);
+
+ void _check_tick_rate_with_current_streamers(const double rate);
+ void _enforce_tick_rate_limits(
+ const size_t change,
+ const double tick_rate,
+ const std::string &direction);
+
+ void _update_tick_rate(const double);
+ void _update_rx_samp_rate(const size_t, const double);
+ void _update_tx_samp_rate(const size_t, const double);
+
+ void _update_time_source(const std::string &source);
+ void _update_clock_source(const std::string &);
+
+ void _update_subdev_spec(
+ const std::string &txrx,
+ const uhd::usrp::subdev_spec_t &spec);
+
+ void _codec_loopback_self_test(uhd::wb_iface::sptr iface);
+
+ void _update_atrs(void);
+ void _update_antenna_sel(const size_t &fe, const std::string &ant);
+ void _update_fe_lo_freq(const std::string &fe, const double freq);
+
+ // overflow handling is special for MIMO case
+ void _handle_overflow(
+ radio_perifs_t &perif,
+ boost::weak_ptr<uhd::rx_streamer> streamer);
+
+
+ // get frontend lock sensor
+ uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx);
+
+ // internal gpios
+ boost::uint8_t _get_internal_gpio(
+ gpio_core_200::sptr,
+ const std::string &);
+
+ void _set_internal_gpio(
+ gpio_core_200::sptr gpio,
+ const std::string &attr,
+ const boost::uint32_t value);
+
+private: // members
+ uhd::device_addr_t _device_addr;
+ xport_t _xport_path;
+ e300_fifo_interface::sptr _fifo_iface;
+ size_t _sid_framer;
+ radio_perifs_t _radio_perifs[2];
+ double _tick_rate;
+ ad9361_ctrl::sptr _codec_ctrl;
+ fe_control_settings_t _settings;
+ global_regs::sptr _global_regs;
+ e300_sensor_manager::sptr _sensor_manager;
+ e300_eeprom_manager::sptr _eeprom_manager;
+ uhd::transport::zero_copy_xport_params _data_xport_params;
+ uhd::transport::zero_copy_xport_params _ctrl_xport_params;
+ gpio_t _misc;
+ gps::ublox::ubx::control::sptr _gps;
+};
+
+}}} // namespace
+
+#endif /* INCLUDED_E300_IMPL_HPP */
diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp
new file mode 100644
index 000000000..dcb6f2afe
--- /dev/null
+++ b/host/lib/usrp/e300/e300_io_impl.cpp
@@ -0,0 +1,599 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_regs.hpp"
+#include "e300_impl.hpp"
+#include "e300_fpga_defs.hpp"
+#include "validate_subdev_spec.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "async_packet_handler.hpp"
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/bind.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <boost/foreach.hpp>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const boost::uint32_t HW_SEQ_NUM_MASK = 0xfff;
+
+/***********************************************************************
+ * update streamer rates
+ **********************************************************************/
+void e300_impl::_check_tick_rate_with_current_streamers(const double rate)
+{
+ size_t max_tx_chan_count = 0, max_rx_chan_count = 0;
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ {
+ boost::shared_ptr<sph::recv_packet_streamer> rx_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(
+ perif.rx_streamer.lock());
+ if (rx_streamer)
+ max_rx_chan_count = std::max(
+ max_rx_chan_count,
+ rx_streamer->get_num_channels());
+ }
+
+ {
+ boost::shared_ptr<sph::send_packet_streamer> tx_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(
+ perif.tx_streamer.lock());
+ if (tx_streamer)
+ max_tx_chan_count = std::max(
+ max_tx_chan_count,
+ tx_streamer->get_num_channels());
+ }
+ }
+ _enforce_tick_rate_limits(max_rx_chan_count, rate, "RX");
+ _enforce_tick_rate_limits(max_tx_chan_count, rate, "TX");
+}
+
+void e300_impl::_update_tick_rate(const double rate)
+{
+ _check_tick_rate_with_current_streamers(rate);
+
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock());
+ if (my_streamer)
+ my_streamer->set_tick_rate(rate);
+ perif.framer->set_tick_rate(_tick_rate);
+ }
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());
+ if (my_streamer)
+ my_streamer->set_tick_rate(rate);
+ perif.deframer->set_tick_rate(_tick_rate);
+ }
+}
+
+void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock());
+ if (my_streamer)
+ my_streamer->set_samp_rate(rate);
+}
+
+void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock());
+ if (my_streamer)
+ my_streamer->set_samp_rate(rate);
+}
+
+/***********************************************************************
+ * frontend selection
+ **********************************************************************/
+void e300_impl::_update_subdev_spec(
+ const std::string &txrx,
+ const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size())
+ validate_subdev_spec(_tree, spec, "rx");
+
+ UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS);
+
+ if (spec.size() >= 1)
+ {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A" or spec[0].sd_name == "B");
+ }
+ if (spec.size() == 2)
+ {
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(
+ (spec[0].sd_name == "A" and spec[1].sd_name == "B") or
+ (spec[0].sd_name == "B" and spec[1].sd_name == "A")
+ );
+ }
+
+ std::vector<size_t> chan_to_dsp_map(spec.size(), 0);
+ for (size_t i = 0; i < spec.size(); i++)
+ chan_to_dsp_map[i] = (spec[i].sd_name == "A") ? 0 : 1;
+ _tree->access<std::vector<size_t> >("/mboards/0" / (txrx + "_chan_dsp_mapping")).set(chan_to_dsp_map);
+
+ const fs_path mb_path = "/mboards/0";
+
+ if (txrx == "tx") {
+ for (size_t i = 0; i < spec.size(); i++)
+ {
+ const std::string conn = _tree->access<std::string>(
+ mb_path / "dboards" / spec[i].db_name /
+ ("tx_frontends") / spec[i].sd_name / "connection").get();
+ _radio_perifs[i].tx_fe->set_mux(conn);
+ }
+
+ } else {
+ for (size_t i = 0; i < spec.size(); i++)
+ {
+ const std::string conn = _tree->access<std::string>(
+ mb_path / "dboards" / spec[i].db_name /
+ ("rx_frontends") / spec[i].sd_name / "connection").get();
+
+ const bool fe_swapped = (conn == "QI" or conn == "Q");
+ _radio_perifs[i].ddc->set_mux(conn, fe_swapped);
+ _radio_perifs[i].rx_fe->set_mux(fe_swapped);
+ }
+ }
+
+ this->_update_enables();
+}
+
+/***********************************************************************
+ * VITA stuff
+ **********************************************************************/
+static void e300_if_hdr_unpack_le(
+ const boost::uint32_t *packet_buff,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ return vrt::if_hdr_unpack_le(packet_buff, if_packet_info);
+}
+
+static void e300_if_hdr_pack_le(
+ boost::uint32_t *packet_buff,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ return vrt::if_hdr_pack_le(packet_buff, if_packet_info);
+}
+
+/***********************************************************************
+ * RX flow control handler
+ **********************************************************************/
+struct e300_rx_fc_cache_t
+{
+ e300_rx_fc_cache_t():
+ last_seq_in(0){}
+ size_t last_seq_in;
+};
+
+void e300_impl::_handle_overflow(
+ radio_perifs_t &perif,
+ boost::weak_ptr<uhd::rx_streamer> streamer)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(streamer.lock());
+
+ //If the rx_streamer has expired then overflow handling makes no sense.
+ if (not my_streamer)
+ return;
+
+ if (my_streamer->get_num_channels() == 1) {
+ perif.framer->handle_overflow();
+ return;
+ }
+
+ // MIMO overflow recovery time
+ // find out if we were in continuous mode before stopping
+ const bool in_continuous_streaming_mode = perif.framer->in_continuous_streaming_mode();
+ // stop streaming
+ my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ // flush transports
+ my_streamer->flush_all(0.001);
+ // restart streaming
+ if (in_continuous_streaming_mode) {
+ stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ stream_cmd.stream_now = false;
+ stream_cmd.time_spec = perif.time64->get_time_now() + time_spec_t(0.01);
+ my_streamer->issue_stream_cmd(stream_cmd);
+ }
+}
+
+
+static void handle_rx_flowctrl(
+ const boost::uint32_t sid,
+ zero_copy_if::sptr xport,
+ boost::shared_ptr<e300_rx_fc_cache_t> fc_cache,
+ const size_t last_seq)
+{
+ static const size_t RXFC_PACKET_LEN_IN_WORDS = 2;
+ static const size_t RXFC_CMD_CODE_OFFSET = 0;
+ static const size_t RXFC_SEQ_NUM_OFFSET = 1;
+
+ managed_send_buffer::sptr buff = xport->get_send_buff(1.0);
+ if (not buff)
+ {
+ throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer");
+ }
+ boost::uint32_t *pkt = buff->cast<boost::uint32_t *>();
+
+ //recover seq32
+ size_t& seq_sw = fc_cache->last_seq_in;
+ const size_t seq_hw = seq_sw & HW_SEQ_NUM_MASK;
+ if (last_seq < seq_hw)
+ seq_sw += (HW_SEQ_NUM_MASK + 1);
+ seq_sw &= ~HW_SEQ_NUM_MASK;
+ seq_sw |= last_seq;
+
+ //load packet info
+ vrt::if_packet_info_t packet_info;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
+ packet_info.num_payload_words32 = RXFC_PACKET_LEN_IN_WORDS;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = seq_sw;
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = sid;
+ packet_info.has_sid = true;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = false;
+ packet_info.has_tlr = false;
+
+ //load header
+ e300_if_hdr_pack_le(pkt, packet_info);
+
+ //load payload
+ pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htowx<boost::uint32_t>(0);
+ pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET] = uhd::htowx<boost::uint32_t>(seq_sw);
+
+ //send the buffer over the interface
+ buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32));
+}
+
+
+/***********************************************************************
+ * TX flow control handler
+ **********************************************************************/
+struct e300_tx_fc_cache_t
+{
+ e300_tx_fc_cache_t(void):
+ stream_channel(0),
+ device_channel(0),
+ last_seq_out(0),
+ last_seq_ack(0),
+ seq_queue(1){}
+ size_t stream_channel;
+ size_t device_channel;
+ size_t last_seq_out;
+ size_t last_seq_ack;
+ bounded_buffer<size_t> seq_queue;
+ boost::shared_ptr<e300_impl::async_md_type> async_queue;
+ boost::shared_ptr<e300_impl::async_md_type> old_async_queue;
+};
+
+#define E300_ASYNC_EVENT_CODE_FLOW_CTRL 0
+
+typedef boost::function<double(void)> tick_rate_retriever_t;
+
+
+static void handle_tx_async_msgs(boost::shared_ptr<e300_tx_fc_cache_t> fc_cache,
+ zero_copy_if::sptr xport,
+ boost::function<double(void)> get_tick_rate)
+{
+ managed_recv_buffer::sptr buff = xport->get_recv_buff();
+ if (not buff)
+ return;
+
+ //extract packet info
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+
+ //unpacking can fail
+ try
+ {
+ e300_if_hdr_unpack_le(packet_buff, if_packet_info);
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl;
+ return;
+ }
+
+ //catch the flow control packets and react
+ if (uhd::wtohx(packet_buff[if_packet_info.num_header_words32+0]) == 0)
+ {
+ const size_t seq = uhd::wtohx(packet_buff[if_packet_info.num_header_words32+1]);
+ fc_cache->seq_queue.push_with_haste(seq);
+ return;
+ }
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::wtohx<boost::uint32_t>,
+ metadata, if_packet_info, packet_buff,
+ get_tick_rate(), fc_cache->stream_channel);
+
+ //The FC response and the burst ack are two indicators that the radio
+ //consumed packets. Use them to update the FC metadata
+ if (metadata.event_code == E300_ASYNC_EVENT_CODE_FLOW_CTRL or
+ metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK
+ ) {
+ const size_t seq = metadata.user_payload[0];
+ fc_cache->seq_queue.push_with_pop_on_full(seq);
+ }
+
+ //FC responses don't propagate up to the user so filter them here
+ if (metadata.event_code != E300_ASYNC_EVENT_CODE_FLOW_CTRL) {
+ fc_cache->async_queue->push_with_pop_on_full(metadata);
+ metadata.channel = fc_cache->device_channel;
+ fc_cache->old_async_queue->push_with_pop_on_full(metadata);
+ standard_async_msg_prints(metadata);
+ }
+}
+
+static managed_send_buffer::sptr get_tx_buff_with_flowctrl(
+ task::sptr /*holds ref*/,
+ boost::shared_ptr<e300_tx_fc_cache_t> fc_cache,
+ zero_copy_if::sptr xport,
+ const size_t fc_window,
+ const double timeout
+){
+ while (true)
+ {
+ const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK);
+ if ((delta & HW_SEQ_NUM_MASK) <= fc_window)
+ break;
+
+ const bool ok = fc_cache->seq_queue.pop_with_timed_wait(fc_cache->last_seq_ack, timeout);
+ if (not ok)
+ return managed_send_buffer::sptr(); //timeout waiting for flow control
+ }
+
+ managed_send_buffer::sptr buff = xport->get_send_buff(timeout);
+ if (buff) {
+ fc_cache->last_seq_out++; //update seq, this will actually be a send
+ }
+
+ return buff;
+}
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+bool e300_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+)
+{
+ return _async_md->pop_with_timed_wait(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr e300_impl::get_rx_stream(const uhd::stream_args_t &args_)
+{
+ boost::mutex::scoped_lock lock(_stream_spawn_mutex);
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (not args.otw_format.empty() and args.otw_format != "sc16")
+ {
+ throw uhd::value_error("e300_impl::get_rx_stream only supports otw_format sc16");
+ }
+ args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+
+ const size_t radio_index = _tree->access<std::vector<size_t> >("/mboards/0/rx_chan_dsp_mapping")
+ .get().at(args.channels[stream_i]);
+
+ radio_perifs_t &perif = _radio_perifs[radio_index];
+
+ // make a transport, grab a sid
+ boost::uint32_t data_sid;
+ both_xports_t data_xports = _make_transport(
+ radio_index ? E300_XB_DST_R1 : E300_XB_DST_R0,
+ E300_RADIO_DEST_PREFIX_RX,
+ _data_xport_params,
+ data_sid);
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::num_vrl_words32*sizeof(boost::uint32_t)
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = data_xports.recv->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer)
+ my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_unpacker(&e300_if_hdr_unpack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_le";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.framer->set_nsamps_per_packet(spp); //seems to be a good place to set this
+ perif.framer->set_sid((data_sid << 16) | (data_sid >> 16));
+ perif.framer->setup(args);
+ perif.ddc->setup(args);
+ my_streamer->set_xport_chan_get_buff(stream_i, boost::bind(
+ &zero_copy_if::get_recv_buff, data_xports.recv, _1
+ ), true /*flush*/);
+ my_streamer->set_overflow_handler(stream_i,
+ boost::bind(&rx_vita_core_3000::handle_overflow, perif.framer)
+ );
+
+ //setup flow control
+ const size_t fc_window = data_xports.recv->get_num_recv_frames();
+ perif.framer->configure_flow_control(fc_window);
+ boost::shared_ptr<e300_rx_fc_cache_t> fc_cache(new e300_rx_fc_cache_t());
+ my_streamer->set_xport_handle_flowctrl(stream_i,
+ boost::bind(&handle_rx_flowctrl, data_sid, data_xports.send, fc_cache, _1),
+ static_cast<size_t>(static_cast<double>(fc_window) * E300_RX_SW_BUFF_FULLNESS),
+ true/*init*/);
+
+ my_streamer->set_issue_stream_cmd(stream_i,
+ boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)
+ );
+ perif.rx_streamer = my_streamer; //store weak pointer
+
+ //sets all tick and samp rates on this streamer
+ this->_update_tick_rate(this->_get_tick_rate());
+ _tree->access<double>(str(boost::format("/mboards/0/rx_dsps/%u/rate/value") % radio_index)).update();
+
+ }
+ _update_enables();
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr e300_impl::get_tx_stream(const uhd::stream_args_t &args_)
+{
+ boost::mutex::scoped_lock lock(_stream_spawn_mutex);
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (not args.otw_format.empty() and args.otw_format != "sc16")
+ {
+ throw uhd::value_error("e300_impl::get_tx_stream only supports otw_format sc16");
+ }
+ args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+
+ //shared async queue for all channels in streamer
+ boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/));
+
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer;
+
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t radio_index = _tree->access<std::vector<size_t> >("/mboards/0/tx_chan_dsp_mapping")
+ .get().at(args.channels[stream_i]);
+
+
+ radio_perifs_t &perif = _radio_perifs[radio_index];
+
+
+ // make a transport, grab a sid
+ boost::uint32_t data_sid;
+ both_xports_t data_xports = _make_transport(
+ radio_index ? E300_XB_DST_R1 : E300_XB_DST_R0,
+ E300_RADIO_DEST_PREFIX_TX,
+ _data_xport_params,
+ data_sid);
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::num_vrl_words32*sizeof(boost::uint32_t)
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = data_xports.send->get_send_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer)
+ my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_packer(&e300_if_hdr_pack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_le";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.deframer->clear();
+ perif.deframer->setup(args);
+ perif.duc->setup(args);
+
+ //flow control setup
+ const size_t fc_window = data_xports.send->get_num_send_frames();
+ perif.deframer->configure_flow_control(0/*cycs off*/, fc_window/8/*pkts*/);
+ boost::shared_ptr<e300_tx_fc_cache_t> fc_cache(new e300_tx_fc_cache_t());
+ fc_cache->stream_channel = stream_i;
+ fc_cache->device_channel = args.channels[stream_i];
+ fc_cache->async_queue = async_md;
+ fc_cache->old_async_queue = _async_md;
+
+ tick_rate_retriever_t get_tick_rate_fn = boost::bind(&e300_impl::_get_tick_rate, this);
+
+ task::sptr task = task::make(boost::bind(&handle_tx_async_msgs,
+ fc_cache, data_xports.recv,
+ get_tick_rate_fn));
+
+ my_streamer->set_xport_chan_get_buff(
+ stream_i,
+ boost::bind(&get_tx_buff_with_flowctrl, task, fc_cache, data_xports.send, fc_window, _1)
+ );
+
+ my_streamer->set_async_receiver(
+ boost::bind(&async_md_type::pop_with_timed_wait, async_md, _1, _2)
+ );
+ my_streamer->set_xport_chan_sid(stream_i, true, data_sid);
+ my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet
+ perif.tx_streamer = my_streamer; //store weak pointer
+
+ //sets all tick and samp rates on this streamer
+ this->_update_tick_rate(this->_get_tick_rate());
+ _tree->access<double>(str(boost::format("/mboards/0/tx_dsps/%u/rate/value") % radio_index)).update();
+ }
+ _update_enables();
+ return my_streamer;
+}
+}}} // namespace
diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp
new file mode 100644
index 000000000..883ff0c4f
--- /dev/null
+++ b/host/lib/usrp/e300/e300_network.cpp
@@ -0,0 +1,642 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_network.hpp"
+
+#ifdef E300_NATIVE
+
+#include "e300_impl.hpp"
+
+#include "ad9361_ctrl.hpp"
+
+#include "e300_sensor_manager.hpp"
+#include "e300_fifo_config.hpp"
+#include "e300_spi.hpp"
+#include "e300_i2c.hpp"
+#include "e300_defaults.hpp"
+#include "e300_common.hpp"
+#include "e300_remote_codec_ctrl.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/images.hpp>
+
+#include <boost/asio.hpp>
+#include <boost/thread.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/make_shared.hpp>
+
+#include <fstream>
+
+using namespace uhd;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+namespace fs = boost::filesystem;
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const size_t E300_NETWORK_DEBUG = false;
+
+static inline bool wait_for_recv_ready(int sock_fd, const size_t timeout_ms)
+{
+ //setup timeval for timeout
+ timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = timeout_ms*1000;
+
+ //setup rset for timeout
+ fd_set rset;
+ FD_ZERO(&rset);
+ FD_SET(sock_fd, &rset);
+
+ //call select with timeout on receive socket
+ return ::select(sock_fd+1, &rset, NULL, NULL, &tv) > 0;
+}
+
+static boost::mutex endpoint_mutex;
+
+/***********************************************************************
+ * Receive tunnel - forwards recv interface to send socket
+ **********************************************************************/
+static void e300_recv_tunnel(
+ const std::string &name,
+ uhd::transport::zero_copy_if::sptr recver,
+ boost::shared_ptr<asio::ip::udp::socket> sender,
+ asio::ip::udp::endpoint *endpoint,
+ bool *running
+)
+{
+ asio::ip::udp::endpoint _tx_endpoint;
+ try
+ {
+ while (*running)
+ {
+ //step 1 - get the buffer
+ managed_recv_buffer::sptr buff = recver->get_recv_buff();
+ if (not buff) continue;
+ if (E300_NETWORK_DEBUG) UHD_MSG(status) << name << " got " << buff->size() << std::endl;
+
+ //step 1.5 -- update endpoint
+ {
+ boost::mutex::scoped_lock l(endpoint_mutex);
+ _tx_endpoint = *endpoint;
+ }
+
+ //step 2 - send to the socket
+ sender->send_to(asio::buffer(buff->cast<const void *>(), buff->size()), _tx_endpoint);
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "e300_recv_tunnel exit " << name << " " << ex.what() << std::endl;
+ }
+ catch(...)
+ {
+ UHD_MSG(error) << "e300_recv_tunnel exit " << name << std::endl;
+ }
+ UHD_MSG(status) << "e300_recv_tunnel exit " << name << std::endl;
+ *running = false;
+}
+
+/***********************************************************************
+ * Send tunnel - forwards recv socket to send interface
+ **********************************************************************/
+static void e300_send_tunnel(
+ const std::string &name,
+ boost::shared_ptr<asio::ip::udp::socket> recver,
+ uhd::transport::zero_copy_if::sptr sender,
+ asio::ip::udp::endpoint *endpoint,
+ bool *running
+)
+{
+ asio::ip::udp::endpoint _rx_endpoint;
+ try
+ {
+ while (*running)
+ {
+ //step 1 - get the buffer
+ managed_send_buffer::sptr buff = sender->get_send_buff();
+ if (not buff) continue;
+
+ //step 2 - recv from socket
+ while (not wait_for_recv_ready(recver->native(), 100) and *running){}
+ if (not *running) break;
+ const size_t num_bytes = recver->receive_from(asio::buffer(buff->cast<void *>(), buff->size()), _rx_endpoint);
+ if (E300_NETWORK_DEBUG) UHD_MSG(status) << name << " got " << num_bytes << std::endl;
+
+ //step 2.5 -- update endpoint
+ {
+ boost::mutex::scoped_lock l(endpoint_mutex);
+ *endpoint = _rx_endpoint;
+ }
+
+ //step 3 - commit the buffer
+ buff->commit(num_bytes);
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "e300_send_tunnel exit " << name << " " << ex.what() << std::endl;
+ }
+ catch(...)
+ {
+ UHD_MSG(error) << "e300_send_tunnel exit " << name << std::endl;
+ }
+ UHD_MSG(status) << "e300_send_tunnel exit " << name << std::endl;
+ *running = false;
+}
+
+static void e300_codec_ctrl_tunnel(
+ const std::string &name,
+ boost::shared_ptr<asio::ip::udp::socket> socket,
+ ad9361_ctrl::sptr _codec_ctrl,
+ asio::ip::udp::endpoint *endpoint,
+ bool *running
+)
+{
+ asio::ip::udp::endpoint _endpoint;
+ try
+ {
+ while (*running)
+ {
+ uint8_t in_buff[64] = {};
+ uint8_t out_buff[64] = {};
+
+ const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint);
+
+ typedef e300_remote_codec_ctrl::transaction_t codec_xact_t;
+
+ if (num_bytes < sizeof(codec_xact_t)) {
+ std::cout << "Received short packet of " << num_bytes << std::endl;
+ continue;
+ }
+
+ codec_xact_t *in = reinterpret_cast<codec_xact_t*>(in_buff);
+ codec_xact_t *out = reinterpret_cast<codec_xact_t*>(out_buff);
+ std::memcpy(out, in, sizeof(codec_xact_t));
+
+ std::string which_str;
+ switch (uhd::ntohx<boost::uint32_t>(in->which)) {
+ case codec_xact_t::CHAIN_TX1:
+ which_str = "TX1"; break;
+ case codec_xact_t::CHAIN_TX2:
+ which_str = "TX2"; break;
+ case codec_xact_t::CHAIN_RX1:
+ which_str = "RX1"; break;
+ case codec_xact_t::CHAIN_RX2:
+ which_str = "RX2"; break;
+ default:
+ which_str = ""; break;
+ }
+
+ switch (uhd::ntohx<boost::uint32_t>(in->action)) {
+ case codec_xact_t::ACTION_SET_GAIN:
+ out->gain = _codec_ctrl->set_gain(which_str, in->gain);
+ break;
+ case codec_xact_t::ACTION_SET_CLOCK_RATE:
+ out->rate = _codec_ctrl->set_clock_rate(in->rate);
+ break;
+ case codec_xact_t::ACTION_SET_ACTIVE_CHANS:
+ _codec_ctrl->set_active_chains(
+ uhd::ntohx<boost::uint32_t>(in->bits) & (1<<0),
+ uhd::ntohx<boost::uint32_t>(in->bits) & (1<<1),
+ uhd::ntohx<boost::uint32_t>(in->bits) & (1<<2),
+ uhd::ntohx<boost::uint32_t>(in->bits) & (1<<3));
+ break;
+ case codec_xact_t::ACTION_TUNE:
+ out->freq = _codec_ctrl->tune(which_str, in->freq);
+ break;
+ case codec_xact_t::ACTION_SET_LOOPBACK:
+ _codec_ctrl->data_port_loopback(
+ uhd::ntohx<boost::uint32_t>(in->bits) & 1);
+ break;
+ default:
+ UHD_MSG(status) << "Got unknown request?!" << std::endl;
+ //Zero out actions to fail this request on client
+ out->action = uhd::htonx<boost::uint32_t>(0);
+ }
+
+ socket->send_to(asio::buffer(out_buff, 64), *endpoint);
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "e300_ctrl_tunnel exit " << name << " " << ex.what() << std::endl;
+ }
+ catch(...)
+ {
+ UHD_MSG(error) << "e300_ctrl_tunnel exit " << name << std::endl;
+ }
+ UHD_MSG(status) << "e300_ctrl_tunnel exit " << name << std::endl;
+ *running = false;
+}
+
+static void e300_global_regs_tunnel(
+ const std::string &name,
+ boost::shared_ptr<asio::ip::udp::socket> socket,
+ global_regs::sptr regs,
+ asio::ip::udp::endpoint *endpoint,
+ bool *running
+)
+{
+ UHD_ASSERT_THROW(regs);
+ asio::ip::udp::endpoint _endpoint;
+ try
+ {
+ while (*running)
+ {
+ uint8_t in_buff[16] = {};
+
+ const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint);
+
+ if (num_bytes < 16) {
+ std::cout << "Received short packet: " << num_bytes << std::endl;
+ continue;
+ }
+
+ global_regs_transaction_t *in =
+ reinterpret_cast<global_regs_transaction_t *>(in_buff);
+
+ if(uhd::ntohx<boost::uint32_t>(in->is_poke)) {
+ regs->poke32(uhd::ntohx<boost::uint32_t>(in->addr), uhd::ntohx<boost::uint32_t>(in->data));
+ }
+ else {
+ in->data = uhd::htonx<boost::uint32_t>(regs->peek32(uhd::ntohx<boost::uint32_t>(in->addr)));
+ socket->send_to(asio::buffer(in_buff, 16), *endpoint);
+ }
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "e300_gregs_tunnel exit " << name << " " << ex.what() << std::endl;
+ }
+ catch(...)
+ {
+ UHD_MSG(error) << "e300_gregs_tunnel exit " << name << std::endl;
+ }
+ UHD_MSG(status) << "e300_gregs_tunnel exit " << name << std::endl;
+ *running = false;
+}
+
+static void e300_sensor_tunnel(
+ const std::string &name,
+ boost::shared_ptr<asio::ip::udp::socket> socket,
+ e300_sensor_manager::sptr sensor_manager,
+ asio::ip::udp::endpoint *endpoint,
+ bool *running
+)
+{
+ asio::ip::udp::endpoint _endpoint;
+ try
+ {
+ while (*running)
+ {
+ uint8_t in_buff[128] = {};
+
+ const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint);
+
+ if (num_bytes < sizeof(sensor_transaction_t)) {
+ std::cout << "Received short packet: " << num_bytes << std::endl;
+ continue;
+ }
+
+ uhd::usrp::e300::sensor_transaction_t *in =
+ reinterpret_cast<uhd::usrp::e300::sensor_transaction_t *>(in_buff);
+
+ if (uhd::ntohx(in->which) == ZYNQ_TEMP) {
+ sensor_value_t temp = sensor_manager->get_mb_temp();
+ // TODO: This is ugly ... use proper serialization
+ in->value = uhd::htonx<boost::uint32_t>(
+ e300_sensor_manager::pack_float_in_uint32_t(temp.to_real()));
+ } else if (uhd::ntohx(in->which) == GPS_FOUND) {
+ in->value = uhd::htonx<boost::uint32_t>(
+ sensor_manager->get_gps_found() ? 1 : 0);
+
+ } else if (uhd::ntohx(in->which) == GPS_LOCK) {
+ in->value = uhd::htonx<boost::uint32_t>(
+ sensor_manager->get_gps_lock().to_bool() ? 1 : 0);
+ } else if (uhd::ntohx(in->which) == GPS_TIME) {
+ in->value = uhd::htonx<boost::uint32_t>(
+ sensor_manager->get_gps_time().to_int());
+ } else
+ UHD_MSG(status) << "Got unknown request?!" << std::endl;
+
+ socket->send_to(asio::buffer(in_buff, sizeof(sensor_transaction_t)), *endpoint);
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "e300_sensor_tunnel exit " << name << " " << ex.what() << std::endl;
+ }
+ catch(...)
+ {
+ UHD_MSG(error) << "e300_sensor_tunnel exit " << name << std::endl;
+ }
+ UHD_MSG(status) << "e300_sensor_tunnel exit " << name << std::endl;
+ *running = false;
+}
+
+static void e300_i2c_tunnel(
+ const std::string &name,
+ boost::shared_ptr<asio::ip::udp::socket> socket,
+ uhd::usrp::e300::i2c::sptr i2c,
+ asio::ip::udp::endpoint *endpoint,
+ bool *running
+)
+{
+ UHD_ASSERT_THROW(i2c);
+ asio::ip::udp::endpoint _endpoint;
+ try
+ {
+ while (*running)
+ {
+ uint8_t in_buff[sizeof(uhd::usrp::e300::i2c_transaction_t)];
+
+ const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint);
+
+ if (num_bytes < sizeof(uhd::usrp::e300::i2c_transaction_t)) {
+ std::cout << "Received short packet: " << num_bytes << std::endl;
+ continue;
+ }
+
+ uhd::usrp::e300::i2c_transaction_t *in =
+ reinterpret_cast<uhd::usrp::e300::i2c_transaction_t *>(in_buff);
+
+ // byte addressed accesses go through here
+ if(in->type & i2c::ONEBYTE) {
+ if(in->type & i2c::WRITE) {
+ i2c->set_i2c_reg8(
+ in->addr,
+ uhd::ntohx<boost::uint16_t>(in->reg), in->data);
+ } else {
+ in->data = i2c->get_i2c_reg8(in->addr, uhd::ntohx<boost::uint16_t>(in->reg));
+ socket->send_to(asio::buffer(in_buff, sizeof(in_buff)), *endpoint);
+ }
+
+ // 2 byte addressed accesses go through here
+ } else if (in->type & i2c::TWOBYTE) {
+ if(in->type & i2c::WRITE) {
+ i2c->set_i2c_reg16(
+ in->addr,
+ uhd::ntohx<boost::uint16_t>(in->reg), in->data);
+ } else {
+ in->data = i2c->get_i2c_reg16(in->addr, uhd::ntohx<boost::uint16_t>(in->reg));
+ socket->send_to(asio::buffer(in_buff, sizeof(in_buff)), *endpoint);
+ }
+
+ } else {
+ UHD_MSG(error) << "e300_i2c_tunnel could not handle message." << std::endl;
+ }
+ }
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "e300_i2c_tunnel exit " << name << " " << ex.what() << std::endl;
+ }
+ catch(...)
+ {
+ UHD_MSG(error) << "e300_i2c_tunnel exit " << name << std::endl;
+ }
+ UHD_MSG(status) << "e300_i2c_tunnel exit " << name << std::endl;
+ *running = false;
+}
+
+
+
+
+class network_server_impl : public network_server
+{
+public:
+ network_server_impl(const uhd::device_addr_t &device_addr);
+ virtual ~network_server_impl(void);
+ void run(void);
+
+private:
+ struct xports_t
+ {
+ uhd::transport::zero_copy_if::sptr send_ctrl_xport;
+ uhd::transport::zero_copy_if::sptr recv_ctrl_xport;
+ uhd::transport::zero_copy_if::sptr tx_data_xport;
+ uhd::transport::zero_copy_if::sptr tx_flow_xport;
+ uhd::transport::zero_copy_if::sptr rx_data_xport;
+ uhd::transport::zero_copy_if::sptr rx_flow_xport;
+ };
+
+private:
+ void _run_server(
+ const std::string &port,
+ const std::string &what,
+ const size_t fe);
+
+private:
+ boost::shared_ptr<e300_fifo_interface> _fifo_iface;
+ xports_t _xports[2];
+ boost::shared_ptr<ad9361_ctrl> _codec_ctrl;
+ boost::shared_ptr<global_regs> _global_regs;
+ boost::shared_ptr<e300_sensor_manager> _sensor_manager;
+ boost::shared_ptr<e300_eeprom_manager> _eeprom_manager;
+};
+
+network_server_impl::~network_server_impl(void)
+{
+}
+
+/***********************************************************************
+ * The UDP server itself
+ **********************************************************************/
+void network_server_impl::_run_server(
+ const std::string &port,
+ const std::string &what,
+ const size_t fe)
+{
+ asio::io_service io_service;
+ asio::ip::udp::resolver resolver(io_service);
+ asio::ip::udp::resolver::query query(asio::ip::udp::v4(), "0.0.0.0", port);
+ asio::ip::udp::endpoint endpoint = *resolver.resolve(query);
+
+ //boost::shared_ptr<asio::ip::udp::acceptor> acceptor(new asio::ip::udp::acceptor(io_service, endpoint));
+ while (not boost::this_thread::interruption_requested())
+ {
+ UHD_MSG(status) << "e300 run server on port " << port << " for " << what << std::endl;
+ try
+ {
+ //while (not wait_for_recv_ready(acceptor->native(), 100))
+ //{
+ // if (boost::this_thread::interruption_requested()) return;
+ //}
+ boost::shared_ptr<asio::ip::udp::socket> socket;
+ socket.reset(new asio::ip::udp::socket(io_service, endpoint));
+ //acceptor->accept(*socket);
+ UHD_MSG(status) << "e300 socket accept on port " << port << " for " << what << std::endl;
+ //asio::ip::udp::no_delay option(true);
+ //socket->set_option(option);
+ boost::thread_group tg;
+ bool running = true;
+ xports_t &perif = _xports[fe];
+ if (what == "RX") {
+ tg.create_thread(boost::bind(&e300_recv_tunnel, "RX data tunnel", perif.rx_data_xport, socket, &endpoint, &running));
+ tg.create_thread(boost::bind(&e300_send_tunnel, "RX flow tunnel", socket, perif.rx_flow_xport, &endpoint, &running));
+ }
+ if (what == "TX") {
+ tg.create_thread(boost::bind(&e300_recv_tunnel, "TX flow tunnel", perif.tx_flow_xport, socket, &endpoint, &running));
+ tg.create_thread(boost::bind(&e300_send_tunnel, "TX data tunnel", socket, perif.tx_data_xport, &endpoint, &running));
+ }
+ if (what == "CTRL") {
+ tg.create_thread(boost::bind(&e300_recv_tunnel, "response tunnel", perif.recv_ctrl_xport, socket, &endpoint, &running));
+ tg.create_thread(boost::bind(&e300_send_tunnel, "control tunnel", socket, perif.send_ctrl_xport, &endpoint, &running));
+ }
+ if (what == "CODEC") {
+ tg.create_thread(boost::bind(&e300_codec_ctrl_tunnel, "CODEC tunnel", socket, _codec_ctrl, &endpoint, &running));
+ }
+ if (what == "I2C") {
+ tg.create_thread(boost::bind(&e300_i2c_tunnel, "I2C tunnel", socket, _eeprom_manager->get_i2c_sptr(), &endpoint, &running));
+ }
+ if (what == "GREGS") {
+ tg.create_thread(boost::bind(&e300_global_regs_tunnel, "GREGS tunnel", socket, _global_regs, &endpoint, &running));
+ }
+ if (what == "SENSOR") {
+ tg.create_thread(boost::bind(&e300_sensor_tunnel, "SENSOR tunnel", socket, _sensor_manager, &endpoint, &running));
+ }
+
+ tg.join_all();
+ socket->close();
+ socket.reset();
+ }
+ catch(...){}
+ }
+}
+
+void network_server_impl::run()
+{
+ for(;;)
+ {
+ boost::thread_group tg;
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_RX_PORT0, "RX",0));
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_TX_PORT0, "TX",0));
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CTRL_PORT0, "CTRL",0));
+
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_RX_PORT1, "RX",1));
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_TX_PORT1, "TX",1));
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CTRL_PORT1, "CTRL",1));
+
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_SENSOR_PORT, "SENSOR", 0 /*don't care */));
+
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CODEC_PORT, "CODEC", 0 /*don't care */));
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_GREGS_PORT, "GREGS", 0 /*don't care */));
+ tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_I2C_PORT, "I2C", 0 /*don't care */));
+ tg.join_all();
+ }
+}
+network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr)
+{
+ _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
+ if (not device_addr.has_key("no_reload_fpga")) {
+ // Load FPGA image if provided via args
+ if (device_addr.has_key("fpga")) {
+ common::load_fpga_image(device_addr["fpga"]);
+ // Else load the FPGA image based on the product ID
+ } else {
+ //extract the FPGA path for the e300
+ const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
+ _eeprom_manager->get_mb_eeprom()["product"]);
+ std::string fpga_image;
+ switch(e300_eeprom_manager::get_mb_type(pid)) {
+ case e300_eeprom_manager::USRP_E310_MB:
+ fpga_image = find_image_path(E310_FPGA_FILE_NAME);
+ break;
+ case e300_eeprom_manager::USRP_E300_MB:
+ fpga_image = find_image_path(E300_FPGA_FILE_NAME);
+ break;
+ case e300_eeprom_manager::UNKNOWN:
+ default:
+ UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
+ << std::endl;
+ fpga_image = find_image_path(E300_FPGA_FILE_NAME);
+ break;
+ }
+ common::load_fpga_image(fpga_image);
+ }
+ }
+
+ uhd::transport::zero_copy_xport_params ctrl_xport_params;
+ ctrl_xport_params.recv_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE;
+ ctrl_xport_params.num_recv_frames = e300::DEFAULT_CTRL_NUM_FRAMES;
+ ctrl_xport_params.send_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE;
+ ctrl_xport_params.num_send_frames = e300::DEFAULT_CTRL_NUM_FRAMES;
+
+ uhd::transport::zero_copy_xport_params data_xport_params;
+ data_xport_params.recv_frame_size = device_addr.cast<size_t>("recv_frame_size", e300::DEFAULT_RX_DATA_FRAME_SIZE);
+ data_xport_params.num_recv_frames = device_addr.cast<size_t>("num_recv_frames", e300::DEFAULT_RX_DATA_NUM_FRAMES);
+ data_xport_params.send_frame_size = device_addr.cast<size_t>("send_frame_size", e300::DEFAULT_TX_DATA_FRAME_SIZE);
+ data_xport_params.num_send_frames = device_addr.cast<size_t>("num_send_frames", e300::DEFAULT_TX_DATA_NUM_FRAMES);
+ // until we figure out why this goes wrong we'll keep this hack around
+ data_xport_params.recv_frame_size =
+ std::min(e300::MAX_NET_RX_DATA_FRAME_SIZE, data_xport_params.recv_frame_size);
+ data_xport_params.send_frame_size =
+ std::min(e300::MAX_NET_TX_DATA_FRAME_SIZE, data_xport_params.send_frame_size);
+
+
+ e300_fifo_config_t fifo_cfg;
+ try {
+ fifo_cfg = e300_read_sysfs();
+ } catch (uhd::lookup_error &e) {
+ throw uhd::runtime_error("Failed to get driver parameters from sysfs.");
+ }
+ _fifo_iface = e300_fifo_interface::make(fifo_cfg);
+ _global_regs = global_regs::make(_fifo_iface->get_global_regs_base());
+
+ // static mapping, boooohhhhhh
+ _xports[0].send_ctrl_xport = _fifo_iface->make_send_xport(E300_R0_CTRL_STREAM, ctrl_xport_params);
+ _xports[0].recv_ctrl_xport = _fifo_iface->make_recv_xport(E300_R0_CTRL_STREAM, ctrl_xport_params);
+ _xports[0].tx_data_xport = _fifo_iface->make_send_xport(E300_R0_TX_DATA_STREAM, data_xport_params);
+ _xports[0].tx_flow_xport = _fifo_iface->make_recv_xport(E300_R0_TX_DATA_STREAM, ctrl_xport_params);
+ _xports[0].rx_data_xport = _fifo_iface->make_recv_xport(E300_R0_RX_DATA_STREAM, data_xport_params);
+ _xports[0].rx_flow_xport = _fifo_iface->make_send_xport(E300_R0_RX_DATA_STREAM, ctrl_xport_params);
+
+ _xports[1].send_ctrl_xport = _fifo_iface->make_send_xport(E300_R1_CTRL_STREAM, ctrl_xport_params);
+ _xports[1].recv_ctrl_xport = _fifo_iface->make_recv_xport(E300_R1_CTRL_STREAM, ctrl_xport_params);
+ _xports[1].tx_data_xport = _fifo_iface->make_send_xport(E300_R1_TX_DATA_STREAM, data_xport_params);
+ _xports[1].tx_flow_xport = _fifo_iface->make_recv_xport(E300_R1_TX_DATA_STREAM, ctrl_xport_params);
+ _xports[1].rx_data_xport = _fifo_iface->make_recv_xport(E300_R1_RX_DATA_STREAM, data_xport_params);
+ _xports[1].rx_flow_xport = _fifo_iface->make_send_xport(E300_R1_RX_DATA_STREAM, ctrl_xport_params);
+
+ ad9361_params::sptr client_settings = boost::make_shared<e300_ad9361_client_t>();
+ _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1);
+ // This is horrible ... why do I have to sleep here?
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+ _sensor_manager = e300_sensor_manager::make_local(
+ gps::ublox::ubx::control::make("/dev/ttyPS1", 9600));
+}
+
+}}} // namespace
+
+using namespace uhd::usrp::e300;
+
+network_server::sptr network_server::make(const uhd::device_addr_t &device_addr)
+{
+ return sptr(new network_server_impl(device_addr));
+}
+
+#else
+
+using namespace uhd::usrp::e300;
+
+network_server::sptr network_server::make(const uhd::device_addr_t &)
+{
+ throw uhd::assertion_error("network_server::make() !E300_NATIVE");
+}
+#endif
diff --git a/host/lib/usrp/e300/e300_network.hpp b/host/lib/usrp/e300/e300_network.hpp
new file mode 100644
index 000000000..5a9df0ca7
--- /dev/null
+++ b/host/lib/usrp/e300/e300_network.hpp
@@ -0,0 +1,43 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_NETWORK_HPP
+#define INCLUDED_E300_NETWORK_HPP
+
+#include <string>
+#include <boost/noncopyable.hpp>
+
+#include <uhd/device.hpp>
+
+
+static const std::string E310_FPGA_FILE_NAME = "usrp_e310_fpga.bit";
+static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit";
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class UHD_API network_server : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<network_server> sptr;
+ virtual void run(void) = 0;
+
+ static sptr make(const uhd::device_addr_t &device_addr);
+};
+
+
+}}}
+#endif // INCLUDED_E300_NETWORK_HPP
diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp
new file mode 100644
index 000000000..f99a19b8e
--- /dev/null
+++ b/host/lib/usrp/e300/e300_regs.hpp
@@ -0,0 +1,69 @@
+//
+// Copyright 2012-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_REGS_HPP
+#define INCLUDED_E300_REGS_HPP
+
+#include <boost/cstdint.hpp>
+
+#define TOREG(x) ((x)*4)
+
+#define localparam static const int
+
+localparam SR_TEST = 7;
+localparam SR_SPI = 8;
+localparam SR_GPIO = 16;
+localparam SR_MISC_OUTS = 24;
+localparam SR_READBACK = 32;
+localparam SR_TX_CTRL = 64;
+localparam SR_RX_CTRL = 96;
+localparam SR_TIME = 128;
+localparam SR_RX_DSP = 144;
+localparam SR_TX_DSP = 184;
+localparam SR_LEDS = 196;
+localparam SR_FP_GPIO = 200;
+localparam SR_RX_FRONT = 208;
+localparam SR_TX_FRONT = 216;
+localparam SR_CODEC_IDLE = 250;
+
+
+localparam RB32_SPI = 4;
+localparam RB64_TIME_NOW = 8;
+localparam RB64_TIME_PPS = 16;
+localparam RB32_TEST = 24;
+localparam RB32_FP_GPIO = 32;
+localparam RB64_CODEC_READBACK = 40;
+localparam RB32_RADIO_NUM = 48;
+
+localparam ST_RX_ENABLE = 20;
+localparam ST_TX_ENABLE = 19;
+
+localparam LED_TXRX_TX = 18;
+localparam LED_TXRX_RX = 17;
+localparam LED_RX_RX = 16;
+localparam VCRX_V2 = 15;
+localparam VCRX_V1 = 14;
+localparam VCTXRX_V2 = 13;
+localparam VCTXRX_V1 = 12;
+localparam TX_ENABLEB = 11;
+localparam TX_ENABLEA = 10;
+localparam RXC_BANDSEL = 8;
+localparam RXB_BANDSEL = 6;
+localparam RX_BANDSEL = 3;
+localparam TX_BANDSEL = 0;
+
+#endif /* INCLUDED_E300_REGS_HPP */
diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
new file mode 100644
index 000000000..bcc8ee4cf
--- /dev/null
+++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
@@ -0,0 +1,148 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_remote_codec_ctrl.hpp"
+
+#include <boost/cstdint.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <cstring>
+#include <iostream>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class e300_remote_codec_ctrl_impl : public e300_remote_codec_ctrl
+{
+public:
+ e300_remote_codec_ctrl_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport)
+ {
+ }
+
+ virtual ~e300_remote_codec_ctrl_impl(void)
+ {
+ }
+
+ double set_gain(const std::string &which, const double value)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_GAIN);
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+ _args.gain = value;
+
+ _transact();
+ return _retval.gain;
+ }
+
+ double set_clock_rate(const double rate)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(
+ transaction_t::ACTION_SET_CLOCK_RATE);
+ _args.which = uhd::htonx<boost::uint32_t>(
+ transaction_t::CHAIN_NONE); /*Unused*/
+ _args.rate = rate;
+
+ _transact();
+ return _retval.gain;
+ }
+
+ void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(
+ transaction_t::ACTION_SET_ACTIVE_CHANS);
+ /*Unused*/
+ _args.which = uhd::htonx<boost::uint32_t>(
+ transaction_t::CHAIN_NONE);
+ _args.bits = uhd::htonx<boost::uint32_t>(
+ (tx1 ? (1<<0) : 0) |
+ (tx2 ? (1<<1) : 0) |
+ (rx1 ? (1<<2) : 0) |
+ (rx2 ? (1<<3) : 0));
+
+ _transact();
+ }
+
+ double tune(const std::string &which, const double value)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_TUNE);
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+ _args.freq = value;
+
+ _transact();
+ return _retval.freq;
+ }
+
+ void data_port_loopback(const bool on)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_LOOPBACK);
+ _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/
+ _args.bits = uhd::htonx<boost::uint32_t>(on ? 1 : 0);
+
+ _transact();
+ }
+
+private:
+ void _transact() {
+ {
+ uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0);
+ if (not buff or buff->size() < sizeof(_args))
+ throw std::runtime_error("e300_remote_codec_ctrl_impl send timeout");
+ std::memcpy(buff->cast<void *>(), &_args, sizeof(_args));
+ buff->commit(sizeof(_args));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0);
+ if (not buff or buff->size() < sizeof(_retval))
+ throw std::runtime_error("e300_remote_codec_ctrl_impl recv timeout");
+ std::memcpy(&_retval, buff->cast<const void *>(), sizeof(_retval));
+ }
+
+ if (_args.action != _retval.action)
+ throw std::runtime_error("e300_remote_codec_ctrl_impl trancation failed.");
+ }
+
+ void _clear() {
+ _args.action = 0;
+ _args.which = 0;
+ _args.bits = 0;
+ _retval.action = 0;
+ _retval.which = 0;
+ _retval.bits = 0;
+ }
+
+ uhd::transport::zero_copy_if::sptr _xport;
+ transaction_t _args;
+ transaction_t _retval;
+};
+
+ad9361_ctrl::sptr e300_remote_codec_ctrl::make(uhd::transport::zero_copy_if::sptr xport)
+{
+ return sptr(new e300_remote_codec_ctrl_impl(xport));
+}
+
+}}};
diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp
new file mode 100644
index 000000000..015ad8323
--- /dev/null
+++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_REMOTE_CODEC_CTRL_HPP
+#define INCLUDED_E300_REMOTE_CODEC_CTRL_HPP
+
+#include "ad9361_ctrl.hpp"
+#include <uhd/transport/zero_copy.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class e300_remote_codec_ctrl : public uhd::usrp::ad9361_ctrl
+{
+public:
+ struct transaction_t {
+ boost::uint32_t action;
+ boost::uint32_t which;
+ union {
+ double rate;
+ double gain;
+ double freq;
+ boost::uint64_t bits;
+ };
+
+ //Actions
+ static const boost::uint32_t ACTION_SET_GAIN = 10;
+ static const boost::uint32_t ACTION_SET_CLOCK_RATE = 11;
+ static const boost::uint32_t ACTION_SET_ACTIVE_CHANS = 12;
+ static const boost::uint32_t ACTION_TUNE = 13;
+ static const boost::uint32_t ACTION_SET_LOOPBACK = 14;
+
+ //Values for "which"
+ static const boost::uint32_t CHAIN_NONE = 0;
+ static const boost::uint32_t CHAIN_TX1 = 1;
+ static const boost::uint32_t CHAIN_TX2 = 2;
+ static const boost::uint32_t CHAIN_RX1 = 3;
+ static const boost::uint32_t CHAIN_RX2 = 4;
+ };
+
+ static sptr make(uhd::transport::zero_copy_if::sptr xport);
+};
+
+}}};
+
+#endif /* INCLUDED_E300_REMOTE_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp
new file mode 100644
index 000000000..5e65b8fd3
--- /dev/null
+++ b/host/lib/usrp/e300/e300_sensor_manager.cpp
@@ -0,0 +1,289 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "e300_sensor_manager.hpp"
+
+#include <boost/thread.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+
+#include <cstring>
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class e300_sensor_proxy : public e300_sensor_manager
+{
+public:
+ e300_sensor_proxy(
+ uhd::transport::zero_copy_if::sptr xport) : _xport(xport)
+ {
+ }
+
+ std::vector<std::string> get_sensors()
+ {
+ return boost::assign::list_of("temp")("gps_locked")("gps_time");
+ }
+
+ uhd::sensor_value_t get_sensor(const std::string &key)
+ {
+ if (key == "temp")
+ return get_mb_temp();
+ else if (key == "gps_locked")
+ return get_gps_lock();
+ else if (key == "gps_time")
+ return get_gps_time();
+ else
+ throw uhd::lookup_error(
+ str(boost::format("Invalid sensor %s requested.") % key));
+ }
+
+ uhd::sensor_value_t get_mb_temp(void)
+ {
+ boost::mutex::scoped_lock(_mutex);
+ sensor_transaction_t transaction;
+ transaction.which = uhd::htonx<boost::uint32_t>(ZYNQ_TEMP);
+ {
+ uhd::transport::managed_send_buffer::sptr buff
+ = _xport->get_send_buff(1.0);
+ if (not buff or buff->size() < sizeof(transaction)) {
+ throw uhd::runtime_error("sensor proxy send timeout");
+ }
+ std::memcpy(
+ buff->cast<void *>(),
+ &transaction,
+ sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff
+ = _xport->get_recv_buff(1.0);
+
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("sensor proxy recv timeout");
+
+ std::memcpy(
+ &transaction,
+ buff->cast<const void *>(),
+ sizeof(transaction));
+ }
+ UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == ZYNQ_TEMP);
+ // TODO: Use proper serialization here ...
+ return sensor_value_t(
+ "temp",
+ e300_sensor_manager::unpack_float_from_uint32_t(
+ uhd::ntohx(transaction.value)),
+ "C");
+ }
+
+ uhd::sensor_value_t get_gps_time(void)
+ {
+ boost::mutex::scoped_lock(_mutex);
+ sensor_transaction_t transaction;
+ transaction.which = uhd::htonx<boost::uint32_t>(GPS_TIME);
+ {
+ uhd::transport::managed_send_buffer::sptr buff
+ = _xport->get_send_buff(1.0);
+ if (not buff or buff->size() < sizeof(transaction)) {
+ throw uhd::runtime_error("sensor proxy send timeout");
+ }
+ std::memcpy(
+ buff->cast<void *>(),
+ &transaction,
+ sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff
+ = _xport->get_recv_buff(1.0);
+
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("sensor proxy recv timeout");
+
+ std::memcpy(
+ &transaction,
+ buff->cast<const void *>(),
+ sizeof(transaction));
+ }
+ UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_TIME);
+ // TODO: Use proper serialization here ...
+ return sensor_value_t("GPS epoch time", int(uhd::ntohx<boost::uint32_t>(transaction.value)), "seconds");
+ }
+
+ bool get_gps_found(void)
+ {
+ boost::mutex::scoped_lock(_mutex);
+ sensor_transaction_t transaction;
+ transaction.which = uhd::htonx<boost::uint32_t>(GPS_FOUND);
+ {
+ uhd::transport::managed_send_buffer::sptr buff
+ = _xport->get_send_buff(1.0);
+ if (not buff or buff->size() < sizeof(transaction)) {
+ throw uhd::runtime_error("sensor proxy send timeout");
+ }
+ std::memcpy(
+ buff->cast<void *>(),
+ &transaction,
+ sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff
+ = _xport->get_recv_buff(1.0);
+
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("sensor proxy recv timeout");
+
+ std::memcpy(
+ &transaction,
+ buff->cast<const void *>(),
+ sizeof(transaction));
+ }
+ UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_FOUND);
+ // TODO: Use proper serialization here ...
+ return static_cast<bool>(uhd::ntohx(transaction.value));
+ }
+
+ uhd::sensor_value_t get_gps_lock(void)
+ {
+ boost::mutex::scoped_lock(_mutex);
+ sensor_transaction_t transaction;
+ transaction.which = uhd::htonx<boost::uint32_t>(GPS_LOCK);
+ {
+ uhd::transport::managed_send_buffer::sptr buff
+ = _xport->get_send_buff(1.0);
+ if (not buff or buff->size() < sizeof(transaction)) {
+ throw uhd::runtime_error("sensor proxy send timeout");
+ }
+ std::memcpy(
+ buff->cast<void *>(),
+ &transaction,
+ sizeof(transaction));
+ buff->commit(sizeof(transaction));
+ }
+ {
+ uhd::transport::managed_recv_buffer::sptr buff
+ = _xport->get_recv_buff(1.0);
+
+ if (not buff or buff->size() < sizeof(transaction))
+ throw uhd::runtime_error("sensor proxy recv timeout");
+
+ std::memcpy(
+ &transaction,
+ buff->cast<const void *>(),
+ sizeof(transaction));
+ }
+ UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_LOCK);
+ // TODO: Use proper serialization here ...
+ return sensor_value_t("GPS lock status", static_cast<bool>(uhd::ntohx(transaction.value)), "locked", "unlocked");
+ }
+
+private:
+ uhd::transport::zero_copy_if::sptr _xport;
+ boost::mutex _mutex;
+};
+
+}}} // namespace
+
+using namespace uhd::usrp::e300;
+
+e300_sensor_manager::sptr e300_sensor_manager::make_proxy(
+ uhd::transport::zero_copy_if::sptr xport)
+{
+ return sptr(new e300_sensor_proxy(xport));
+}
+
+#ifdef E300_NATIVE
+#include "e300_fifo_config.hpp"
+
+namespace uhd { namespace usrp { namespace e300 {
+
+static const std::string E300_TEMP_SYSFS = "iio:device0";
+
+class e300_sensor_local : public e300_sensor_manager
+{
+public:
+ e300_sensor_local(uhd::gps_ctrl::sptr gps_ctrl) : _gps_ctrl(gps_ctrl)
+ {
+ }
+
+ std::vector<std::string> get_sensors()
+ {
+ return boost::assign::list_of("temp")("gps_locked")("gps_time");
+ }
+
+ uhd::sensor_value_t get_sensor(const std::string &key)
+ {
+ if (key == "temp")
+ return get_mb_temp();
+ else if (key == "gps_locked")
+ return get_gps_lock();
+ else if (key == "gps_time")
+ return get_gps_time();
+ else
+ throw uhd::lookup_error(
+ str(boost::format("Invalid sensor %s requested.") % key));
+ }
+
+ uhd::sensor_value_t get_mb_temp(void)
+ {
+ double scale = boost::lexical_cast<double>(
+ e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_scale"));
+ unsigned long raw = boost::lexical_cast<unsigned long>(
+ e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_raw"));
+ unsigned long offset = boost::lexical_cast<unsigned long>(
+ e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_offset"));
+ return sensor_value_t("temp", (raw + offset) * scale / 1000, "C");
+ }
+
+ bool get_gps_found(void)
+ {
+ return _gps_ctrl->gps_detected();
+ }
+
+ uhd::sensor_value_t get_gps_lock(void)
+ {
+ return _gps_ctrl->get_sensor("gps_locked");
+ }
+
+ uhd::sensor_value_t get_gps_time(void)
+ {
+ return _gps_ctrl->get_sensor("gps_time");
+ }
+
+private:
+ gps_ctrl::sptr _gps_ctrl;
+};
+}}}
+
+using namespace uhd::usrp::e300;
+e300_sensor_manager::sptr e300_sensor_manager::make_local(
+ uhd::gps_ctrl::sptr gps_ctrl)
+{
+ return sptr(new e300_sensor_local(gps_ctrl));
+}
+
+#else
+using namespace uhd::usrp::e300;
+e300_sensor_manager::sptr e300_sensor_manager::make_local(
+ uhd::gps_ctrl::sptr gps_ctrl)
+{
+ throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE");
+}
+#endif
diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp
new file mode 100644
index 000000000..503a7bb63
--- /dev/null
+++ b/host/lib/usrp/e300/e300_sensor_manager.hpp
@@ -0,0 +1,77 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <boost/noncopyable.hpp>
+#include <boost/cstdint.hpp>
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+
+#ifndef INCLUDED_E300_SENSOR_MANAGER_HPP
+#define INCLUDED_E300_SENSOR_MANAGER_HPP
+
+namespace uhd { namespace usrp { namespace e300 {
+
+struct sensor_transaction_t {
+ boost::uint32_t which;
+ union {
+ boost::uint32_t value;
+ boost::uint32_t value64;
+ };
+};
+
+
+
+enum sensor {ZYNQ_TEMP=0, GPS_FOUND=1, GPS_TIME=2,
+ GPS_LOCK=3};
+
+class e300_sensor_manager : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<e300_sensor_manager> sptr;
+ virtual bool get_gps_found(void) = 0;
+
+ virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0;
+ virtual std::vector<std::string> get_sensors(void) = 0;
+
+ virtual uhd::sensor_value_t get_mb_temp(void) = 0;
+ virtual uhd::sensor_value_t get_gps_lock(void) = 0;
+ virtual uhd::sensor_value_t get_gps_time(void) = 0;
+
+ static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport);
+ static sptr make_local(uhd::gps_ctrl::sptr gps_ctrl);
+
+ // Note: This is a hack
+ static boost::uint32_t pack_float_in_uint32_t(const float &v)
+ {
+ const boost::uint32_t *cast = reinterpret_cast<const uint32_t*>(&v);
+ return *cast;
+ }
+
+ static float unpack_float_from_uint32_t(const boost::uint32_t &v)
+ {
+ const float *cast = reinterpret_cast<const float*>(&v);
+ return *cast;
+ }
+};
+
+
+}}} // namespace
+
+#endif // INCLUDED_E300_SENSOR_MANAGER_HPP
diff --git a/host/lib/usrp/e300/e300_spi.cpp b/host/lib/usrp/e300/e300_spi.cpp
new file mode 100644
index 000000000..9a2daf4a7
--- /dev/null
+++ b/host/lib/usrp/e300/e300_spi.cpp
@@ -0,0 +1,127 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/config.hpp>
+#include <uhd/exception.hpp>
+#include "e300_spi.hpp"
+
+#ifdef E300_NATIVE
+#include <boost/thread.hpp>
+#include <boost/format.hpp>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class spidev_impl : public spi
+{
+public:
+
+ spidev_impl(const std::string &device)
+ : _mode(SPI_CPHA),
+ _speed(2000000),
+ _bits(8),
+ _delay(0)
+ {
+ int ret;
+ _fd = open(device.c_str(), O_RDWR);
+ if (_fd < 0)
+ throw uhd::runtime_error(str(boost::format("Could not open spidev device %s") % device));
+
+ ret = ioctl(_fd, SPI_IOC_WR_MODE, &_mode);
+ if (ret == -1)
+ throw uhd::runtime_error("Could not set spidev mode");
+
+ ret = ioctl(_fd, SPI_IOC_RD_MODE, &_mode);
+ if (ret == -1)
+ throw uhd::runtime_error("Could not get spidev mode");
+
+ ret = ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits);
+ if (ret == -1)
+ throw uhd::runtime_error("Could not set spidev bits per word");
+
+ ret = ioctl(_fd, SPI_IOC_RD_BITS_PER_WORD, &_bits);
+ if (ret == -1)
+ throw uhd::runtime_error("Could not get spidev bits per word");
+
+ ret = ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_speed);
+ if (ret == -1)
+ throw uhd::runtime_error("Could not set spidev max speed");
+
+ ret = ioctl(_fd, SPI_IOC_RD_MAX_SPEED_HZ, &_speed);
+ if (ret == -1)
+ throw uhd::runtime_error("Could not get spidev max speed");
+ }
+
+ virtual ~spidev_impl()
+ {
+ close(_fd);
+ }
+
+ boost::uint32_t transact_spi(int, const uhd::spi_config_t &,
+ boost::uint32_t data, size_t num_bits,
+ bool)
+ {
+ int ret(0);
+ struct spi_ioc_transfer tr;
+
+ uint8_t *tx_data = reinterpret_cast<uint8_t *>(&data);
+
+
+ UHD_ASSERT_THROW(num_bits == 24);
+ uint8_t tx[] = {tx_data[2], tx_data[1], tx_data[0]};
+
+ uint8_t rx[3];
+ tr.tx_buf = (unsigned long) &tx[0];
+ tr.rx_buf = (unsigned long) &rx[0];
+ tr.len = num_bits >> 3;
+ tr.bits_per_word = _bits;
+ tr.speed_hz = _speed;
+ tr.delay_usecs = _delay;
+
+ ret = ioctl(_fd, SPI_IOC_MESSAGE(1), &tr);
+ if (ret < 1)
+ throw uhd::runtime_error("Could not send spidev message");
+
+ return rx[2];
+ }
+
+private:
+ int _fd;
+ boost::uint8_t _mode;
+ boost::uint32_t _speed;
+ boost::uint8_t _bits;
+ boost::uint16_t _delay;
+};
+
+spi::sptr spi::make(const std::string &device)
+{
+ return spi::sptr(new spidev_impl(device));
+}
+}}};
+#else
+namespace uhd { namespace usrp { namespace e300 {
+
+spi::sptr spi::make(const std::string &)
+{
+ throw uhd::assertion_error("spi::make() !E300_NATIVE");
+}
+}}};
+#endif //E300_NATIVE
diff --git a/host/lib/usrp/e300/e300_spi.hpp b/host/lib/usrp/e300/e300_spi.hpp
new file mode 100644
index 000000000..67e990aaa
--- /dev/null
+++ b/host/lib/usrp/e300/e300_spi.hpp
@@ -0,0 +1,34 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_E300_SPI_HPP
+#define INCLUDED_E300_SPI_HPP
+
+#include <uhd/types/serial.hpp>
+
+namespace uhd { namespace usrp { namespace e300 {
+
+class spi : public virtual uhd::spi_iface
+{
+public:
+ typedef boost::shared_ptr<spi> sptr;
+ static sptr make(const std::string &device);
+};
+
+}}};
+
+#endif /* INCLUDED_E300_SPI_HPP */
diff --git a/host/lib/usrp/e300/e300_sysfs_hooks.cpp b/host/lib/usrp/e300/e300_sysfs_hooks.cpp
new file mode 100644
index 000000000..fdeaf0858
--- /dev/null
+++ b/host/lib/usrp/e300/e300_sysfs_hooks.cpp
@@ -0,0 +1,121 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifdef E300_NATIVE
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <libudev.h>
+
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+
+static const std::string E300_AXI_FPGA_SYSFS = "40000000.axi-fpga";
+static const std::string E300_XDEV_SYSFS = "f8007000.ps7-dev-cfg";
+
+std::string e300_get_sysfs_attr(const std::string &node, const std::string &attr)
+{
+ udev *udev;
+ udev_enumerate *enumerate;
+ udev_list_entry *devices, *dev_list_entry;
+ udev_device *dev;
+ std::string retstring;
+
+ udev = udev_new();
+
+ if (!udev) {
+ throw uhd::lookup_error("Failed to get udev handle.");
+ }
+
+ enumerate = udev_enumerate_new(udev);
+ udev_enumerate_add_match_sysname(enumerate, node.c_str());
+ udev_enumerate_scan_devices(enumerate);
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices)
+ {
+ const char *path;
+
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+
+ retstring = udev_device_get_sysattr_value(dev, attr.c_str());
+ if (retstring.size())
+ break;
+ }
+
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return retstring;
+}
+
+static bool e300_fpga_loaded_successfully(void)
+{
+ return boost::lexical_cast<bool>(e300_get_sysfs_attr(E300_XDEV_SYSFS, "prog_done"));
+}
+
+#include "e300_fifo_config.hpp"
+#include <uhd/exception.hpp>
+
+e300_fifo_config_t e300_read_sysfs(void)
+{
+
+ if (not e300_fpga_loaded_successfully())
+ {
+ throw uhd::runtime_error("E300 FPGA load failed!");
+ }
+
+ e300_fifo_config_t config;
+
+ config.buff_length = boost::lexical_cast<unsigned long>(
+ e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "buffer_length"));
+ config.ctrl_length = boost::lexical_cast<unsigned long>(
+ e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "control_length"));
+ config.phys_addr = boost::lexical_cast<unsigned long>(
+ e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "phys_addr"));
+
+ return config;
+}
+
+#else //E300_NATIVE
+
+#include "e300_fifo_config.hpp"
+#include <uhd/exception.hpp>
+
+e300_fifo_config_t e300_read_sysfs(void)
+{
+ throw uhd::assertion_error("e300_read_sysfs() !E300_NATIVE");
+}
+
+std::string e300_get_sysfs_attr(const std::string &, const std::string &)
+{
+ throw uhd::assertion_error("e300_sysfs_attr() !E300_NATIVE");
+}
+
+#endif //E300_NATIVE
diff --git a/host/lib/usrp/e300/e300_ublox_control.hpp b/host/lib/usrp/e300/e300_ublox_control.hpp
new file mode 100644
index 000000000..8705d6c52
--- /dev/null
+++ b/host/lib/usrp/e300/e300_ublox_control.hpp
@@ -0,0 +1,50 @@
+#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP
+#define INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP
+
+#include <boost/cstdint.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/asio.hpp>
+#include <uhd/config.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/types/sensors.hpp>
+
+#include "e300_async_serial.hpp"
+
+namespace uhd { namespace usrp { namespace gps {
+
+namespace ublox { namespace ubx {
+
+class control : public virtual uhd::gps_ctrl
+{
+public:
+ typedef boost::shared_ptr<control> sptr;
+
+ static sptr make(const std::string &node, const size_t baud_rate);
+
+ virtual void configure_message_rate(
+ const boost::uint16_t msg,
+ const boost::uint8_t rate) = 0;
+
+ virtual void configure_antenna(
+ const boost::uint16_t flags,
+ const boost::uint16_t pins) = 0;
+
+ virtual void configure_pps(
+ const boost::uint32_t interval,
+ const boost::uint32_t length,
+ const boost::int8_t status,
+ const boost::uint8_t time_ref,
+ const boost::uint8_t flags,
+ const boost::int16_t antenna_delay,
+ const boost::int16_t rf_group_delay,
+ const boost::int32_t user_delay) = 0;
+
+ virtual void configure_rates(
+ boost::uint16_t meas_rate,
+ boost::uint16_t nav_rate,
+ boost::uint16_t time_ref) = 0;
+};
+}} // namespace ublox::ubx
+
+}}} // namespace
+#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP
diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.cpp b/host/lib/usrp/e300/e300_ublox_control_impl.cpp
new file mode 100644
index 000000000..a0ec10271
--- /dev/null
+++ b/host/lib/usrp/e300/e300_ublox_control_impl.cpp
@@ -0,0 +1,505 @@
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/assign/list_of.hpp>
+#include "boost/date_time/posix_time/posix_time.hpp"
+
+#include <iostream>
+
+
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+
+#include "e300_ublox_control.hpp"
+
+#ifdef E300_NATIVE
+#include "e300_ublox_control_impl.hpp"
+
+
+namespace uhd { namespace usrp { namespace gps {
+
+namespace ublox { namespace ubx {
+
+control_impl::control_impl(const std::string &node, const size_t baud_rate)
+{
+ _decode_init();
+ _serial = boost::make_shared<async_serial>(node, baud_rate);
+ _serial->set_read_callback(boost::bind(&control_impl::_rx_callback, this, _1, _2));
+
+ _detect();
+
+ configure_message_rate(MSG_GLL, 0);
+ configure_message_rate(MSG_GSV, 0);
+ configure_message_rate(MSG_GGA, 0);
+ configure_message_rate(MSG_GSA, 0);
+ configure_message_rate(MSG_RMC, 0);
+ configure_message_rate(MSG_VTG, 0);
+ configure_message_rate(MSG_NAV_TIMEUTC, 1);
+ configure_message_rate(MSG_NAV_SOL, 1);
+
+ configure_antenna(0x001b, 0x8251);
+
+ configure_pps(0xf4240, 0x3d090, 1, 0 /* utc */, 1, 0, 0, 0);
+
+ _sensors = boost::assign::list_of("gps_locked")("gps_time");
+}
+
+bool control_impl::gps_detected(void)
+{
+ return _detected;
+}
+
+void control_impl::_detect(void)
+{
+ _send_message(MSG_MON_VER, NULL, 0);
+}
+
+std::vector<std::string> control_impl::get_sensors(void)
+{
+ return _sensors;
+}
+
+uhd::sensor_value_t control_impl::get_sensor(std::string key)
+{
+ if (key == "gps_time") {
+ return sensor_value_t("GPS epoch time", int(_get_epoch_time()), "seconds");
+ } else if (key == "gps_locked") {
+ bool lock;
+ _locked.wait_and_see(lock);
+ return sensor_value_t("GPS lock status", lock, "locked", "unlocked");
+ } else
+ throw uhd::key_error(str(boost::format("sensor %s unknown.") % key));
+}
+
+std::time_t control_impl::_get_epoch_time(void)
+{
+ boost::posix_time::ptime ptime;
+ _ptime.wait_and_see(ptime);
+ return (ptime - boost::posix_time::from_time_t(0)).total_seconds();
+}
+
+control_impl::~control_impl(void)
+{
+ // turn it all off again
+ configure_antenna(0x001a, 0x8251);
+ configure_pps(0xf4240, 0x3d090, 1, 1, 0, 0, 0, 0);
+}
+
+void control_impl::_decode_init(void)
+{
+ _decode_state = DECODE_SYNC1;
+ _rx_ck_a = 0;
+ _rx_ck_b = 0;
+ _rx_payload_length = 0;
+ _rx_payload_index = 0;
+}
+
+void control_impl::_add_byte_to_checksum(const boost::uint8_t b)
+{
+ _rx_ck_a = _rx_ck_a + b;
+ _rx_ck_b = _rx_ck_b + _rx_ck_a;
+}
+
+void control_impl::_calc_checksum(
+ const boost::uint8_t *buffer,
+ const boost::uint16_t length,
+ checksum_t &checksum)
+{
+ for (size_t i = 0; i < length; i++)
+ {
+ checksum.ck_a = checksum.ck_a + buffer[i];
+ checksum.ck_b = checksum.ck_b + checksum.ck_a;
+ }
+}
+
+void control_impl::configure_rates(
+ boost::uint16_t meas_rate,
+ boost::uint16_t nav_rate,
+ boost::uint16_t time_ref)
+{
+ payload_tx_cfg_rate_t cfg_rate;
+ cfg_rate.meas_rate = uhd::htowx<boost::uint16_t>(meas_rate);
+ cfg_rate.nav_rate = uhd::htowx<boost::uint16_t>(nav_rate);
+ cfg_rate.time_ref = uhd::htowx<boost::uint16_t>(time_ref);
+
+ _send_message(
+ MSG_CFG_RATE,
+ reinterpret_cast<const uint8_t*>(&cfg_rate),
+ sizeof(cfg_rate));
+
+ _wait_for_ack(MSG_CFG_RATE, 1.0);
+}
+
+void control_impl::configure_message_rate(
+ const boost::uint16_t msg,
+ const uint8_t rate)
+{
+ payload_tx_cfg_msg_t cfg_msg;
+ cfg_msg.msg = uhd::htowx<boost::uint16_t>(msg);
+ cfg_msg.rate[0] = 0;//rate;
+ cfg_msg.rate[1] = rate;
+ cfg_msg.rate[2] = 0;//rate;
+ cfg_msg.rate[3] = 0;//rate;
+ cfg_msg.rate[4] = 0;//rate;
+ cfg_msg.rate[5] = 0;//rate;
+ _send_message(
+ MSG_CFG_MSG,
+ reinterpret_cast<const uint8_t*>(&cfg_msg),
+ sizeof(cfg_msg));
+
+ _wait_for_ack(MSG_CFG_MSG, 1.0);
+}
+
+void control_impl::configure_antenna(
+ const boost::uint16_t flags,
+ const boost::uint16_t pins)
+{
+ payload_tx_cfg_ant_t cfg_ant;
+ cfg_ant.pins = uhd::htowx<boost::uint16_t>(pins);
+ cfg_ant.flags = uhd::htowx<boost::uint16_t>(flags);
+ _send_message(
+ MSG_CFG_ANT,
+ reinterpret_cast<const uint8_t*>(&cfg_ant),
+ sizeof(cfg_ant));
+ if (_wait_for_ack(MSG_CFG_ANT, 1.0) < 0) {
+ throw uhd::runtime_error("Didn't get an ACK for antenna configuration.");
+ }
+
+}
+
+void control_impl::configure_pps(
+ const boost::uint32_t interval,
+ const boost::uint32_t length,
+ const boost::int8_t status,
+ const boost::uint8_t time_ref,
+ const boost::uint8_t flags,
+ const boost::int16_t antenna_delay,
+ const boost::int16_t rf_group_delay,
+ const boost::int32_t user_delay)
+{
+ payload_tx_cfg_tp_t cfg_tp;
+ cfg_tp.interval = uhd::htowx<boost::uint32_t>(interval);
+ cfg_tp.length = uhd::htowx<boost::uint32_t>(length);
+ cfg_tp.status = status;
+ cfg_tp.time_ref = time_ref;
+ cfg_tp.flags = flags;
+ cfg_tp.antenna_delay = uhd::htowx<boost::int16_t>(antenna_delay);
+ cfg_tp.rf_group_delay = uhd::htowx<boost::int16_t>(rf_group_delay);
+ cfg_tp.user_delay = uhd::htowx<boost::int32_t>(user_delay);
+ _send_message(
+ MSG_CFG_TP,
+ reinterpret_cast<const uint8_t*>(&cfg_tp),
+ sizeof(cfg_tp));
+ if (_wait_for_ack(MSG_CFG_TP, 1.0) < 0) {
+ throw uhd::runtime_error("Didn't get an ACK for PPS configuration.");
+ }
+}
+
+
+void control_impl::_rx_callback(const char *data, unsigned int len)
+{
+ //std::cout << "IN RX CALLBACK" << std::flush << std::endl;
+ std::vector<char> v(data, data+len);
+ BOOST_FOREACH(const char &c, v)
+ {
+ _parse_char(c);
+ }
+}
+
+void control_impl::_parse_char(const boost::uint8_t b)
+{
+ int ret = 0;
+
+ switch (_decode_state) {
+
+ // we're expecting the first sync byte
+ case DECODE_SYNC1:
+ if (b == SYNC1) { // sync1 found goto next step
+ _decode_state = DECODE_SYNC2;
+ } // else stay around
+ break;
+
+ // we're expecting the second sync byte
+ case DECODE_SYNC2:
+ if (b == SYNC2) { // sync2 found goto next step
+ _decode_state = DECODE_CLASS;
+ } else {
+ // failed, reset
+ _decode_init();
+ }
+ break;
+
+ // we're expecting the class byte
+ case DECODE_CLASS:
+ _add_byte_to_checksum(b);
+ _rx_msg = b;
+ _decode_state = DECODE_ID;
+ break;
+
+ // we're expecting the id byte
+ case DECODE_ID:
+ _add_byte_to_checksum(b);
+ _rx_msg |= (b << 8);
+ _decode_state = DECODE_LENGTH1;
+ break;
+
+ // we're expecting the first length byte
+ case DECODE_LENGTH1:
+ _add_byte_to_checksum(b);
+ _rx_payload_length = b;
+ _decode_state = DECODE_LENGTH2;
+ break;
+
+ // we're expecting the second length byte
+ case DECODE_LENGTH2:
+ _add_byte_to_checksum(b);
+ _rx_payload_length |= (b << 8);
+ if(_payload_rx_init()) {
+ _decode_init(); // we failed, give up for this one
+ } else {
+ _decode_state = _rx_payload_length ?
+ DECODE_PAYLOAD : DECODE_CHKSUM1;
+ }
+ break;
+
+ // we're expecting payload
+ case DECODE_PAYLOAD:
+ _add_byte_to_checksum(b);
+ switch(_rx_msg) {
+ default:
+ ret = _payload_rx_add(b);
+ break;
+ };
+ if (ret < 0) {
+ // we couldn't deal with the payload, discard the whole thing
+ _decode_init();
+ } else if (ret > 0) {
+ // payload was complete, let's check the checksum;
+ _decode_state = DECODE_CHKSUM1;
+ } else {
+ // more payload expected, don't move
+ }
+ ret = 0;
+ break;
+
+ case DECODE_CHKSUM1:
+ if (_rx_ck_a != b) {
+ // checksum didn't match, barf
+ std::cout << boost::format("Failed checksum byte1 %lx != %lx")
+ % int(_rx_ck_a) % int(b) << std::endl;
+ _decode_init();
+ } else {
+ _decode_state = DECODE_CHKSUM2;
+ }
+ break;
+
+ case DECODE_CHKSUM2:
+ if (_rx_ck_b != b) {
+ // checksum didn't match, barf
+ std::cout << boost::format("Failed checksum byte2 %lx != %lx")
+ % int(_rx_ck_b) % int(b) << std::endl;
+
+ } else {
+ ret = _payload_rx_done(); // payload done
+ }
+ _decode_init();
+ break;
+
+ default:
+ break;
+ };
+}
+
+int control_impl::_payload_rx_init(void)
+{
+ int ret = 0;
+
+ _rx_state = RXMSG_HANDLE; // by default handle
+ switch(_rx_msg) {
+
+ case MSG_NAV_SOL:
+ if (not (_rx_payload_length == sizeof(payload_rx_nav_sol_t)))
+ _rx_state = RXMSG_ERROR_LENGTH;
+ break;
+
+ case MSG_NAV_TIMEUTC:
+ if (not (_rx_payload_length == sizeof(payload_rx_nav_timeutc_t)))
+ _rx_state = RXMSG_ERROR_LENGTH;
+ break;
+
+ case MSG_MON_VER:
+ break; // always take this one
+
+ case MSG_ACK_ACK:
+ if (not (_rx_payload_length == sizeof(payload_rx_ack_ack_t)))
+ _rx_state = RXMSG_ERROR_LENGTH;
+ break;
+
+ case MSG_ACK_NAK:
+ if (not (_rx_payload_length == sizeof(payload_rx_ack_nak_t)))
+ _rx_state = RXMSG_ERROR_LENGTH;
+ break;
+
+ default:
+ _rx_state = RXMSG_DISABLE;
+ break;
+ };
+
+ switch (_rx_state) {
+ case RXMSG_HANDLE: // handle message
+ case RXMSG_IGNORE: // ignore message but don't report error
+ ret = 0;
+ break;
+ case RXMSG_DISABLE: // ignore message but don't report error
+ case RXMSG_ERROR_LENGTH: // the length doesn't match
+ ret = -1;
+ break;
+ default: // invalid, error
+ ret = -1;
+ break;
+ };
+
+ return ret;
+}
+
+int control_impl::_payload_rx_add(const boost::uint8_t b)
+{
+ int ret = 0;
+ _buf.raw[_rx_payload_index] = b;
+ if (++_rx_payload_index >= _rx_payload_length)
+ ret = 1;
+ return ret;
+}
+
+int control_impl::_payload_rx_done(void)
+{
+ int ret = 0;
+ if (_rx_state != RXMSG_HANDLE) {
+ return 0;
+ }
+
+ switch (_rx_msg) {
+ case MSG_MON_VER:
+ _detected = true;
+ break;
+
+ case MSG_MON_HW:
+ std::cout << "MON-HW" << std::endl;
+ break;
+
+ case MSG_ACK_ACK:
+ if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_ack.msg == _ack_waiting_msg))
+ _ack_state = ACK_GOT_ACK;
+ break;
+
+ case MSG_ACK_NAK:
+ if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_nak.msg == _ack_waiting_msg))
+ _ack_state = ACK_GOT_NAK;
+
+ break;
+
+ case MSG_CFG_ANT:
+ break;
+
+ case MSG_NAV_TIMEUTC:
+ _ptime.update(boost::posix_time::ptime(
+ boost::gregorian::date(
+ boost::gregorian::greg_year(uhd::wtohx<boost::uint16_t>(
+ _buf.payload_rx_nav_timeutc.year)),
+ boost::gregorian::greg_month(_buf.payload_rx_nav_timeutc.month),
+ boost::gregorian::greg_day(_buf.payload_rx_nav_timeutc.day)),
+ (boost::posix_time::hours(_buf.payload_rx_nav_timeutc.hour)
+ + boost::posix_time::minutes(_buf.payload_rx_nav_timeutc.min)
+ + boost::posix_time::seconds(_buf.payload_rx_nav_timeutc.sec))));
+ break;
+
+ case MSG_NAV_SOL:
+ _locked.update(_buf.payload_rx_nav_sol.gps_fix > 0);
+ break;
+
+ default:
+ std::cout << boost::format("Got unknown message %lx , with good checksum [") % int(_rx_msg);
+ for(size_t i = 0; i < _rx_payload_length; i++)
+ std::cout << boost::format("%lx, ") % int(_buf.raw[i]);
+ std::cout << "]"<< std::endl;
+ break;
+ };
+ return ret;
+}
+
+void control_impl::_send_message(
+ const boost::uint16_t msg,
+ const boost::uint8_t *payload,
+ const boost::uint16_t len)
+{
+ header_t header = {SYNC1, SYNC2, msg, len};
+ checksum_t checksum = {0, 0};
+
+ // calculate checksums, first header without sync
+ // then payload
+ _calc_checksum(
+ reinterpret_cast<boost::uint8_t*>(&header) + 2,
+ sizeof(header) - 2, checksum);
+ if (payload)
+ _calc_checksum(payload, len, checksum);
+
+ _serial->write(
+ reinterpret_cast<const char*>(&header),
+ sizeof(header));
+
+ if (payload)
+ _serial->write((const char *) payload, len);
+
+ _serial->write(
+ reinterpret_cast<const char*>(&checksum),
+ sizeof(checksum));
+}
+
+int control_impl::_wait_for_ack(
+ const boost::uint16_t msg,
+ const double timeout)
+{
+ int ret = -1;
+
+ _ack_state = ACK_WAITING;
+ _ack_waiting_msg = msg;
+
+ boost::system_time timeout_time =
+ boost::get_system_time() +
+ boost::posix_time::milliseconds(timeout * 1000.0);
+
+ do {
+ if(_ack_state == ACK_GOT_ACK)
+ return 0;
+ else if (_ack_state == ACK_GOT_NAK) {
+ return -1;
+ }
+ boost::this_thread::sleep(boost::posix_time::milliseconds(20));
+ } while (boost::get_system_time() < timeout_time);
+
+ // we get here ... it's a timeout
+ _ack_state = ACK_IDLE;
+ return ret;
+}
+
+
+}} // namespace ublox::ubx
+}}} // namespace
+
+using namespace uhd::usrp::gps::ublox::ubx;
+
+control::sptr control::make(const std::string &node, const size_t baud_rate)
+{
+ return control::sptr(new control_impl(node, baud_rate));
+}
+#else
+using namespace uhd::usrp::gps::ublox::ubx;
+
+control::sptr control::make(const std::string &node, const size_t baud_rate)
+{
+ throw uhd::assertion_error("control::sptr::make: !E300_NATIVE");
+}
+#endif // E300_NATIVE
diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.hpp b/host/lib/usrp/e300/e300_ublox_control_impl.hpp
new file mode 100644
index 000000000..a1dcbfe6c
--- /dev/null
+++ b/host/lib/usrp/e300/e300_ublox_control_impl.hpp
@@ -0,0 +1,457 @@
+#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP
+#define INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP
+
+#include <boost/cstdint.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/asio.hpp>
+#include <uhd/config.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/types/sensors.hpp>
+
+#include "e300_async_serial.hpp"
+
+namespace uhd { namespace usrp { namespace gps {
+
+namespace ublox { namespace ubx {
+// ublox binary sync words
+static const boost::uint8_t SYNC1 = 0xB5;
+static const boost::uint8_t SYNC2 = 0x62;
+
+// message classes
+static const boost::uint8_t CLASS_NAV = 0x01;
+static const boost::uint8_t CLASS_ACK = 0x05;
+static const boost::uint8_t CLASS_CFG = 0x06;
+static const boost::uint8_t CLASS_MON = 0x0a;
+static const boost::uint8_t CLASS_NMEA = 0xf0;
+
+// Message IDs
+static const boost::uint8_t ID_NAV_POSLLH = 0x02;
+static const boost::uint8_t ID_NAV_SOL = 0x06;
+static const boost::uint8_t ID_NAV_PVT = 0x07;
+static const boost::uint8_t ID_NAV_VELNED = 0x12;
+static const boost::uint8_t ID_NAV_TIMEUTC = 0x21;
+static const boost::uint8_t ID_NAV_SVINFO = 0x30;
+static const boost::uint8_t ID_ACK_NAK = 0x00;
+static const boost::uint8_t ID_ACK_ACK = 0x01;
+static const boost::uint8_t ID_CFG_PRT = 0x00;
+static const boost::uint8_t ID_CFG_ANT = 0x13;
+static const boost::uint8_t ID_CFG_TP = 0x07;
+static const boost::uint8_t ID_CFG_MSG = 0x01;
+static const boost::uint8_t ID_CFG_RATE = 0x08;
+static const boost::uint8_t ID_CFG_NAV5 = 0x24;
+static const boost::uint8_t ID_MON_VER = 0x04;
+static const boost::uint8_t ID_MON_HW = 0x09;
+static const boost::uint8_t ID_GGA = 0x00;
+static const boost::uint8_t ID_GLL = 0x01;
+static const boost::uint8_t ID_GSA = 0x02;
+static const boost::uint8_t ID_GSV = 0x03;
+static const boost::uint8_t ID_RMC = 0x04;
+static const boost::uint8_t ID_VTG = 0x05;
+static const boost::uint8_t ID_GST = 0x07;
+
+// Message Classes & IDs //
+static const boost::uint16_t MSG_NAV_POSLLH
+ = CLASS_NAV | (ID_NAV_POSLLH << 8);
+static const boost::uint16_t MSG_NAV_SOL
+ = CLASS_NAV | (ID_NAV_SOL << 8);
+static const boost::uint16_t MSG_NAV_PVT
+ = CLASS_NAV | (ID_NAV_PVT << 8);
+static const boost::uint16_t MSG_NAV_VELNED
+ = CLASS_NAV | (ID_NAV_VELNED << 8);
+static const boost::uint16_t MSG_NAV_TIMEUTC
+ = CLASS_NAV | (ID_NAV_TIMEUTC << 8);
+static const boost::uint16_t MSG_NAV_SVINFO
+ = CLASS_NAV | (ID_NAV_SVINFO << 8);
+static const boost::uint16_t MSG_ACK_NAK
+ = CLASS_ACK | (ID_ACK_NAK << 8);
+static const boost::uint16_t MSG_ACK_ACK
+ = CLASS_ACK | (ID_ACK_ACK << 8);
+static const boost::uint16_t MSG_CFG_PRT
+ = CLASS_CFG | (ID_CFG_PRT << 8);
+static const boost::uint16_t MSG_CFG_ANT
+ = CLASS_CFG | (ID_CFG_ANT << 8);
+static const boost::uint16_t MSG_CFG_TP
+ = CLASS_CFG | (ID_CFG_TP << 8);
+static const boost::uint16_t MSG_CFG_MSG
+ = CLASS_CFG | (ID_CFG_MSG << 8);
+static const boost::uint16_t MSG_CFG_RATE
+ = CLASS_CFG | (ID_CFG_RATE << 8);
+static const boost::uint16_t MSG_CFG_NAV5
+ = CLASS_CFG | (ID_CFG_NAV5 << 8);
+static const boost::uint16_t MSG_MON_HW
+ = CLASS_MON | (ID_MON_HW << 8);
+static const boost::uint16_t MSG_MON_VER
+ = CLASS_MON | (ID_MON_VER << 8);
+
+// NMEA ones
+static const boost::uint16_t MSG_GGA
+ = CLASS_NMEA | (ID_GGA << 8);
+static const boost::uint16_t MSG_GLL
+ = CLASS_NMEA | (ID_GLL << 8);
+static const boost::uint16_t MSG_GSA
+ = CLASS_NMEA | (ID_GSA << 8);
+static const boost::uint16_t MSG_GSV
+ = CLASS_NMEA | (ID_GSV << 8);
+static const boost::uint16_t MSG_RMC
+ = CLASS_NMEA | (ID_RMC << 8);
+static const boost::uint16_t MSG_VTG
+ = CLASS_NMEA | (ID_VTG << 8);
+
+// header
+struct header_t
+{
+ boost::uint8_t sync1;
+ boost::uint8_t sync2;
+ boost::uint16_t msg;
+ boost::uint16_t length;
+};
+
+// checksum
+struct checksum_t
+{
+ boost::uint8_t ck_a;
+ boost::uint8_t ck_b;
+};
+
+// rx rx mon-hw (ubx6)
+struct payload_rx_mon_hw_t
+{
+ boost::uint32_t pin_sel;
+ boost::uint32_t pin_bank;
+ boost::uint32_t pin_dir;
+ boost::uint32_t pin_val;
+ boost::uint16_t noise_per_ms;
+ boost::uint16_t agc_cnt;
+ boost::uint8_t a_status;
+ boost::uint8_t a_power;
+ boost::uint8_t flags;
+ boost::uint8_t reserved1;
+ boost::uint32_t used_mask;
+ boost::uint8_t vp[25];
+ boost::uint8_t jam_ind;
+ boost::uint16_t reserved3;
+ boost::uint32_t pin_irq;
+ boost::uint32_t pullh;
+ boost::uint32_t pulll;
+};
+
+// rx mon-ver
+struct payload_rx_mon_ver_part1_t
+{
+ char sw_version[30];
+ char hw_version[10];
+};
+
+struct payload_rx_mon_ver_part2_t
+{
+ boost::uint8_t extension[30];
+};
+
+// rx ack-ack
+typedef union {
+ boost::uint16_t msg;
+ struct {
+ boost::uint8_t cls_id;
+ boost::uint8_t msg_id;
+ };
+} payload_rx_ack_ack_t;
+
+// rx ack-nak
+typedef union {
+ boost::uint16_t msg;
+ struct {
+ boost::uint8_t cls_id;
+ boost::uint8_t msg_id;
+ };
+} payload_rx_ack_nak_t;
+
+// tx cfg-prt (uart)
+struct payload_tx_cfg_prt_t
+{
+ boost::uint8_t port_id;
+ boost::uint8_t reserved0;
+ boost::uint16_t tx_ready;
+ boost::uint32_t mode;
+ boost::uint32_t baud_rate;
+ boost::uint16_t in_proto_mask;
+ boost::uint16_t out_proto_mask;
+ boost::uint16_t flags;
+ boost::uint16_t reserved5;
+};
+
+// tx cfg-rate
+struct payload_tx_cfg_rate_t
+{
+ boost::uint16_t meas_rate;
+ boost::uint16_t nav_rate;
+ boost::uint16_t time_ref;
+};
+
+// tx cfg-msg
+struct payload_tx_cfg_msg_t
+{
+ boost::uint16_t msg;
+ boost::uint8_t rate[6];
+};
+
+
+// tx cfg-ant
+struct payload_tx_cfg_ant_t
+{
+ boost::uint16_t flags;
+ boost::uint16_t pins;
+};
+
+// tx cfg-tp
+struct payload_tx_cfg_tp_t
+{
+ boost::uint32_t interval;
+ boost::uint32_t length;
+ boost::int8_t status;
+ boost::uint8_t time_ref;
+ boost::uint8_t flags;
+ boost::uint8_t reserved1;
+ boost::int16_t antenna_delay;
+ boost::int16_t rf_group_delay;
+ boost::int32_t user_delay;
+};
+
+struct payload_rx_nav_sol_t
+{
+ boost::uint32_t i_tow;
+ boost::int32_t f_tow;
+ boost::int16_t week;
+ boost::uint8_t gps_fix;
+ boost::uint8_t flags;
+ boost::int32_t ecef_x;
+ boost::int32_t ecef_y;
+ boost::int32_t ecef_z;
+ boost::uint32_t p_acc;
+ boost::int32_t ecef_vx;
+ boost::int32_t ecef_vy;
+ boost::int32_t ecef_vz;
+ boost::uint32_t s_acc;
+ boost::uint16_t p_dop;
+ boost::uint8_t reserved1;
+ boost::uint8_t num_sv;
+ boost::uint32_t reserved2;
+};
+
+struct payload_rx_nav_timeutc_t
+{
+ boost::uint32_t i_tow;
+ boost::uint32_t t_acc;
+ boost::int32_t nano;
+ boost::uint16_t year;
+ boost::uint8_t month;
+ boost::uint8_t day;
+ boost::uint8_t hour;
+ boost::uint8_t min;
+ boost::uint8_t sec;
+ boost::uint8_t valid;
+};
+
+typedef union {
+ payload_rx_mon_hw_t payload_rx_mon_hw;
+
+ payload_rx_mon_ver_part1_t payload_rx_mon_ver_part1;
+ payload_rx_mon_ver_part2_t payload_rx_mon_ver_part2;
+
+ payload_rx_ack_ack_t payload_rx_ack_ack;
+ payload_rx_ack_nak_t payload_rx_ack_nak;
+
+ payload_tx_cfg_prt_t payload_tx_cfg_prt;
+ payload_tx_cfg_ant_t payload_tx_cfg_ant;
+ payload_tx_cfg_rate_t payload_tx_cfg_rate;
+
+ payload_tx_cfg_msg_t payload_tx_cfg_msg;
+
+ payload_rx_nav_timeutc_t payload_rx_nav_timeutc;
+ payload_rx_nav_sol_t payload_rx_nav_sol;
+ boost::uint8_t raw[];
+} buf_t;
+
+
+template <typename T>
+class sensor_entry
+{
+public:
+ sensor_entry() : _seen(false)
+ {
+ }
+
+ void update(const T &val)
+ {
+ boost::mutex::scoped_lock l(_mutex);
+ _value = val;
+ _seen = false;
+ l.unlock();
+ _cond.notify_one();
+ }
+
+ bool seen() const
+ {
+ boost::mutex::scoped_lock l(_mutex);
+ return _seen;
+ }
+
+ bool try_and_see(T &val)
+ {
+ boost::mutex::scoped_lock l(_mutex);
+ if (_seen)
+ return false;
+
+ val = _value;
+ _seen = true;
+ return true;
+ }
+
+ void wait_and_see(T &val)
+ {
+ boost::mutex::scoped_lock l(_mutex);
+ while(_seen)
+ {
+ _cond.wait(l);
+ //std::cout << "Already seen ... " << std::endl;
+ }
+ val = _value;
+ _seen = true;
+ }
+
+private: // members
+ T _value;
+ boost::mutex _mutex;
+ boost::condition_variable _cond;
+ bool _seen;
+};
+
+class control_impl : public control
+{
+public:
+ control_impl(const std::string &node, const size_t baud_rate);
+
+ virtual ~control_impl(void);
+
+ void configure_message_rate(
+ const boost::uint16_t msg,
+ const boost::uint8_t rate);
+
+ void configure_antenna(
+ const boost::uint16_t flags,
+ const boost::uint16_t pins);
+
+ void configure_pps(
+ const boost::uint32_t interval,
+ const boost::uint32_t length,
+ const boost::int8_t status,
+ const boost::uint8_t time_ref,
+ const boost::uint8_t flags,
+ const boost::int16_t antenna_delay,
+ const boost::int16_t rf_group_delay,
+ const boost::int32_t user_delay);
+
+ void configure_rates(
+ boost::uint16_t meas_rate,
+ boost::uint16_t nav_rate,
+ boost::uint16_t time_ref);
+
+ // gps_ctrl interface
+ bool gps_detected(void);
+ std::vector<std::string> get_sensors(void);
+ uhd::sensor_value_t get_sensor(std::string key);
+
+private: // types
+ enum decoder_state_t {
+ DECODE_SYNC1 = 0,
+ DECODE_SYNC2,
+ DECODE_CLASS,
+ DECODE_ID,
+ DECODE_LENGTH1,
+ DECODE_LENGTH2,
+ DECODE_PAYLOAD,
+ DECODE_CHKSUM1,
+ DECODE_CHKSUM2,
+ };
+
+ enum rxmsg_state_t {
+ RXMSG_IGNORE = 0,
+ RXMSG_HANDLE,
+ RXMSG_DISABLE,
+ RXMSG_ERROR_LENGTH
+ };
+
+ enum ack_state_t {
+ ACK_IDLE = 0,
+ ACK_WAITING,
+ ACK_GOT_ACK,
+ ACK_GOT_NAK
+ };
+
+private: // methods
+ std::time_t _get_epoch_time(void);
+
+ void _decode_init(void);
+
+ void _add_byte_to_checksum(const boost::uint8_t b);
+
+ void _detect(void);
+
+ void _send_message(
+ const boost::uint16_t msg,
+ const boost::uint8_t *payload,
+ const boost::uint16_t len);
+
+ int _wait_for_ack(
+ const boost::uint16_t msg,
+ const double timeout);
+
+ void _calc_checksum(
+ const boost::uint8_t *buffer,
+ const boost::uint16_t length,
+ checksum_t &checksum);
+
+ void _rx_callback(const char *data, unsigned len);
+
+ void _parse_char(const boost::uint8_t b);
+
+ int _payload_rx_init(void);
+
+ int _payload_rx_add(const boost::uint8_t b);
+
+ int _payload_rx_done(void);
+
+private: // members
+ // gps_ctrl stuff
+ bool _detected;
+ std::vector<std::string> _sensors;
+
+ sensor_entry<bool> _locked;
+ sensor_entry<boost::posix_time::ptime> _ptime;
+
+ // decoder state
+ decoder_state_t _decode_state;
+ rxmsg_state_t _rxmsg_state;
+
+ ack_state_t _ack_state;
+ boost::uint16_t _ack_waiting_msg;
+
+ boost::uint8_t _rx_ck_a;
+ boost::uint8_t _rx_ck_b;
+
+ boost::uint16_t _rx_payload_length;
+ size_t _rx_payload_index;
+ boost::uint16_t _rx_msg;
+
+ rxmsg_state_t _rx_state;
+
+ boost::shared_ptr<async_serial> _serial;
+
+ // this has to be at the end of the
+ // class to be valid C++
+ buf_t _buf;
+};
+
+}} // namespace ublox::ubx
+
+}}} // namespace
+#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
index 11bb2488a..f693ee7a6 100644
--- a/host/utils/CMakeLists.txt
+++ b/host/utils/CMakeLists.txt
@@ -35,6 +35,13 @@ SET(x3xx_burner_sources
cdecode.c
)
+find_package(UDev)
+IF(ENABLE_E300)
+ IF(UDEV_FOUND)
+ LIST(APPEND util_runtime_sources usrp_e3x0_network_mode.cpp)
+ ENDIF(UDEV_FOUND)
+ENDIF(ENABLE_E300)
+
#for each source: build an executable and install
FOREACH(util_source ${util_runtime_sources})
GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE)
diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp
index 9a1556650..05f918eb4 100644
--- a/host/utils/query_gpsdo_sensors.cpp
+++ b/host/utils/query_gpsdo_sensors.cpp
@@ -107,15 +107,20 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
const time_t pc_clock_time = time(NULL);
const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps();
if (last_pps_time.to_ticks(1.0) == gps_time.to_int()) {
- std::cout << boost::format("GPS and UHD Device time are aligned.\n");
- } else
- std::cout << boost::format("\nGPS and UHD Device time are NOT aligned. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n");
-
+ std::cout << boost::format("GPS and UHD Device time are aligned.\n");
+ } else {
+ std::cout << boost::format("\nGPS and UHD Device time are NOT aligned last_pps: %ld vs gps: %ld. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n") % last_pps_time.to_ticks(1.0) % gps_time.to_int() << std::endl;
+ }
+
//print NMEA strings
- std::cout << boost::format("Printing available NMEA strings:\n");
- uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga");
- uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc");
- std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string();
+ try {
+ uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga");
+ uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc");
+ std::cout << boost::format("Printing available NMEA strings:\n");
+ std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string();
+ } catch (std::exception &e) {
+ std::cout << "NMEA strings not implemented for this device." << std::endl;
+ }
std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs());
std::cout << boost::format("PC Clock time: %.0f seconds\n") % pc_clock_time;
diff --git a/host/utils/usrp_e3x0_network_mode.cpp b/host/utils/usrp_e3x0_network_mode.cpp
new file mode 100644
index 000000000..dae4b6ff7
--- /dev/null
+++ b/host/utils/usrp_e3x0_network_mode.cpp
@@ -0,0 +1,80 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../lib/usrp/e300/e300_network.hpp"
+#include <uhd/device.hpp>
+#include <uhd/exception.hpp>
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/transport/if_addrs.hpp>
+
+#include <boost/program_options.hpp>
+#include <boost/format.hpp>
+#include <boost/asio.hpp>
+
+#include <iostream>
+
+namespace po = boost::program_options;
+
+static void check_network_ok(void)
+{
+ using namespace uhd::transport;
+ using namespace boost::asio::ip;
+ std::vector<if_addrs_t> addrs = get_if_addrs();
+
+ if(addrs.size() == 1 and addrs.at(0).inet == address_v4::loopback().to_string())
+ throw uhd::runtime_error(
+ "No network address except for loopback found.\n"
+ "Make sure your DHCP server is working or configure a static IP");
+}
+
+int main(int argc, char *argv[])
+{
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "help message")
+ ("fpga", po::value<std::string>(), "fpga image to load")
+ ;
+
+ po::variables_map vm;
+ po::store(po::parse_command_line(argc, argv, desc), vm);
+ po::notify(vm);
+
+ //print the help message
+ if (vm.count("help")){
+ std::cout << boost::format("UHD E3x0 Network Mode %s") % desc << std::endl;
+ return EXIT_FAILURE;
+ }
+ uhd::device_addr_t args;
+ if(vm.count("fpga")) {
+ args["fpga"] = vm["fpga"].as<std::string>();
+ }
+
+ try {
+ check_network_ok();
+ uhd::usrp::e300::network_server::sptr server = uhd::usrp::e300::network_server::make(args);
+ server->run();
+ } catch (uhd::assertion_error &e) {
+ UHD_MSG(error) << "This executable is supposed to run on the device, not on the host." << std::endl
+ << "Please refer to the manual section on operating your e3x0 device in network mode." << std::endl;
+ return EXIT_FAILURE;
+ } catch (uhd::runtime_error &e) {
+ UHD_MSG(error) << e.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}