Initial upstream commit
The history contained unlicensed code so everything got squashed, sorry.
Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/common/hw-gfx-gma-i2c.adb b/common/hw-gfx-gma-i2c.adb
new file mode 100644
index 0000000..ec3afc2
--- /dev/null
+++ b/common/hw-gfx-gma-i2c.adb
@@ -0,0 +1,234 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+
+with HW.Time;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word8;
+
+package body HW.GFX.GMA.I2C is
+
+ PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL : constant := 1 * 2 ** 31;
+
+ GMBUS0_GMBUS_RATE_SELECT_MASK : constant := 7 * 2 ** 8;
+ GMBUS0_GMBUS_RATE_SELECT_100KHZ : constant := 0 * 2 ** 8;
+ GMBUS0_GMBUS_RATE_SELECT_50KHZ : constant := 1 * 2 ** 8;
+ GMBUS0_PIN_PAIR_SELECT_MASK : constant := 7 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_NONE : constant := 0 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_DAC : constant := 2 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_LVDS : constant := 3 * 2 ** 0;
+ -- Order is C, B, D: no typo!
+ GMBUS0_PIN_PAIR_SELECT_DIGI_C : constant := 4 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_DIGI_B : constant := 5 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_DIGI_D : constant := 6 * 2 ** 0;
+
+ GMBUS1_SOFTWARE_CLEAR_INTERRUPT : constant := 1 * 2 ** 31;
+ GMBUS1_SOFTWARE_READY : constant := 1 * 2 ** 30;
+ GMBUS1_ENABLE_TIMEOUT : constant := 1 * 2 ** 29;
+ GMBUS1_BUS_CYCLE_SELECT_MASK : constant := 7 * 2 ** 25;
+ GMBUS1_BUS_CYCLE_STOP : constant := 1 * 2 ** 27;
+ GMBUS1_BUS_CYCLE_INDEX : constant := 1 * 2 ** 26;
+ GMBUS1_BUS_CYCLE_WAIT : constant := 1 * 2 ** 25;
+ GMBUS1_TOTAL_BYTE_COUNT_MASK : constant := 511 * 2 ** 16;
+ GMBUS1_TOTAL_BYTE_COUNT_SHIFT : constant := 16;
+ GMBUS1_8BIT_SLAVE_INDEX_MASK : constant := 255 * 2 ** 8;
+ GMBUS1_8BIT_SLAVE_INDEX_SHIFT : constant := 8;
+ GMBUS1_SLAVE_ADDRESS_MASK : constant := 127 * 2 ** 1;
+ GMBUS1_SLAVE_ADDRESS_SHIFT : constant := 1;
+ GMBUS1_DIRECTION_MASK : constant := 1 * 2 ** 0;
+ GMBUS1_DIRECTION_WRITE : constant := 0 * 2 ** 0;
+ GMBUS1_DIRECTION_READ : constant := 1 * 2 ** 0;
+
+ GMBUS2_INUSE : constant := 1 * 2 ** 15;
+ GMBUS2_HARDWARE_WAIT_PHASE : constant := 1 * 2 ** 14;
+ GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant := 1 * 2 ** 13;
+ GMBUS2_GMBUS_INTERRUPT_STATUS : constant := 1 * 2 ** 12;
+ GMBUS2_HARDWARE_READY : constant := 1 * 2 ** 11;
+ GMBUS2_NAK_INDICATOR : constant := 1 * 2 ** 10;
+ GMBUS2_GMBUS_ACTIVE : constant := 1 * 2 ** 9;
+ GMBUS2_CURRENT_BYTE_COUNT_MASK : constant := 511 * 2 ** 0;
+
+ GMBUS4_INTERRUPT_MASK : constant := 31 * 2 ** 0;
+
+ GMBUS5_2BYTE_INDEX_ENABLE : constant := 1 * 2 ** 31;
+
+ function GMBUS1_TOTAL_BYTE_COUNT
+ (Count : HW.GFX.I2C.Transfer_Length)
+ return Word32 is
+ begin
+ return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
+ end GMBUS1_TOTAL_BYTE_COUNT;
+
+ function GMBUS1_SLAVE_ADDRESS
+ (Address : HW.GFX.I2C.Transfer_Address)
+ return Word32 is
+ begin
+ return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
+ end GMBUS1_SLAVE_ADDRESS;
+
+ function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
+ begin
+ return
+ (case Port is
+ when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
+ when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
+ when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
+ when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
+ when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
+ when others => GMBUS0_PIN_PAIR_SELECT_NONE);
+ end GMBUS0_PIN_PAIR_SELECT;
+
+ ----------------------------------------------------------------------------
+
+ procedure GMBUS_Ready (Result : out Boolean)
+ is
+ GMBUS2 : Word32;
+ begin
+ Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
+ Result := (GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
+ GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
+ GMBUS2_GMBUS_INTERRUPT_STATUS or
+ GMBUS2_NAK_INDICATOR)) = 0;
+ end GMBUS_Ready;
+
+ procedure Reset_GMBUS (Success : out Boolean) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Registers.PCH_GMBUS1, GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
+ Registers.Write (Registers.PCH_GMBUS1, 0);
+ Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
+
+ GMBUS_Ready (Success);
+ end Reset_GMBUS;
+
+ procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Ungate_GMBUS_Unit_Level then
+ Registers.Set_Mask
+ (Register => Registers.PCH_DSPCLK_GATE_D,
+ Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
+ end if;
+
+ -- TODO: Refactor + check for timeout.
+ Registers.Wait_Unset_Mask (Registers.PCH_GMBUS2, GMBUS2_INUSE);
+
+ GMBUS_Ready (Success);
+ if not Success then
+ Reset_GMBUS (Success);
+ end if;
+
+ if Success then
+ Registers.Write
+ (Register => Registers.PCH_GMBUS0,
+ Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
+ GMBUS0_PIN_PAIR_SELECT (Port));
+ Registers.Write
+ (Register => Registers.PCH_GMBUS4,
+ Value => 0);
+ Registers.Write
+ (Register => Registers.PCH_GMBUS5,
+ Value => 0);
+ end if;
+ end Init_GMBUS;
+
+ procedure Release_GMBUS
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
+
+ -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
+ Registers.Write (Registers.PCH_GMBUS2, GMBUS2_INUSE);
+
+ if Config.Ungate_GMBUS_Unit_Level then
+ Registers.Unset_Mask
+ (Register => Registers.PCH_DSPCLK_GATE_D,
+ Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
+ end if;
+ end Release_GMBUS;
+
+ procedure I2C_Read
+ (Port : in PCH_Port;
+ Address : in HW.GFX.I2C.Transfer_Address;
+ Length : in out HW.GFX.I2C.Transfer_Length;
+ Data : out HW.GFX.I2C.Transfer_Data;
+ Success : out Boolean)
+ is
+ GMBUS2,
+ GMBUS3 : Word32;
+
+ Current : HW.GFX.I2C.Transfer_Length;
+ Transfered : HW.GFX.I2C.Transfer_Length := 0;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Data := (others => 0);
+
+ Init_GMBUS (Port, Success);
+ if Success then
+ Registers.Write
+ (Register => Registers.PCH_GMBUS1,
+ Value => GMBUS1_SOFTWARE_READY or
+ GMBUS1_BUS_CYCLE_INDEX or
+ GMBUS1_BUS_CYCLE_WAIT or
+ GMBUS1_TOTAL_BYTE_COUNT (Length) or
+ GMBUS1_SLAVE_ADDRESS (Address) or
+ GMBUS1_DIRECTION_READ);
+
+ while Success and then Transfered < Length loop
+ Registers.Wait_Set_Mask
+ (Register => Registers.PCH_GMBUS2,
+ Mask => GMBUS2_HARDWARE_READY,
+ TOut_MS => 55);
+
+ Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
+ Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
+ (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
+ if Success then
+ Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
+
+ Registers.Read (Registers.PCH_GMBUS3, GMBUS3);
+ for I in Transfered .. Current - 1 loop
+ Data (I) := Byte (GMBUS3 and 16#ff#);
+ GMBUS3 := Shift_Right (GMBUS3, 8);
+ end loop;
+ Transfered := Current;
+ end if;
+ end loop;
+ if Success then
+ Registers.Wait_Set_Mask
+ (Register => Registers.PCH_GMBUS2,
+ Mask => GMBUS2_HARDWARE_WAIT_PHASE);
+ Registers.Write
+ (Register => Registers.PCH_GMBUS1,
+ Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.PCH_GMBUS2,
+ Mask => GMBUS2_GMBUS_ACTIVE);
+ end if;
+ end if;
+ Length := Transfered;
+
+ Release_GMBUS;
+ end I2C_Read;
+
+end HW.GFX.GMA.I2C;