aboutsummaryrefslogtreecommitdiffstats
path: root/firmware
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 /firmware
parentfd3e84941de463fa1a7ebab0a69515b4bf2614cd (diff)
downloaduhd-1b149f561370687ad65e3aa644a402f00dbd16ea.tar.gz
uhd-1b149f561370687ad65e3aa644a402f00dbd16ea.tar.bz2
uhd-1b149f561370687ad65e3aa644a402f00dbd16ea.zip
Initial commit E300 support.
Diffstat (limited to 'firmware')
-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
42 files changed, 8401 insertions, 0 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