From: Matt Johnston Date: Thu, 8 Sep 2022 05:26:00 +0000 (+0800) Subject: gpio: Add interrupts and trigger registers X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=52da535b16e3211405ab530dd645cc4362abee0f;p=microwatt.git gpio: Add interrupts and trigger registers Allows to trigger on rising/falling/both edge, as well as high/low level. Registers are compatible with Linux ftgpio010 driver. Signed-off-by: Matt Johnston --- diff --git a/gpio.vhdl b/gpio.vhdl index d5454a2..3df610f 100644 --- a/gpio.vhdl +++ b/gpio.vhdl @@ -39,20 +39,37 @@ architecture behaviour of gpio is constant GPIO_REG_DATA_SET : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00100"; constant GPIO_REG_DATA_CLR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00101"; + constant GPIO_REG_INT_EN : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01000"; + constant GPIO_REG_INT_STAT : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01001"; + -- write 1 to clear + constant GPIO_REG_INT_CLR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01100"; + -- edge 0, level 1 + constant GPIO_REG_INT_TYPE : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01101"; + -- for edge: trigger on either edge = 1 + constant GPIO_REG_INT_BOTH_EDGE : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01110"; + -- for edge: rising 0, falling 1 + -- for level: high 0, low 1 + constant GPIO_REG_INT_LEVEL : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01111"; + -- Current output value and direction signal reg_data : std_ulogic_vector(NGPIO - 1 downto 0); signal reg_dirn : std_ulogic_vector(NGPIO - 1 downto 0); + signal reg_in0 : std_ulogic_vector(NGPIO - 1 downto 0); signal reg_in1 : std_ulogic_vector(NGPIO - 1 downto 0); signal reg_in2 : std_ulogic_vector(NGPIO - 1 downto 0); + signal reg_intr_en : std_ulogic_vector(NGPIO - 1 downto 0); + signal reg_intr_hit : std_ulogic_vector(NGPIO - 1 downto 0); + signal reg_intr_type : std_ulogic_vector(NGPIO - 1 downto 0); + signal reg_intr_level : std_ulogic_vector(NGPIO - 1 downto 0); + signal reg_intr_both : std_ulogic_vector(NGPIO - 1 downto 0); + signal wb_rsp : wb_io_slave_out; signal reg_out : std_ulogic_vector(NGPIO - 1 downto 0); + constant ZEROS : std_ulogic_vector(NGPIO-1 downto 0) := (others => '0'); begin - - -- No interrupt facility for now - intr <= '0'; - + intr <= '0' when reg_intr_hit = ZEROS else '1'; gpio_out <= reg_data; gpio_dir <= reg_dirn; @@ -60,22 +77,62 @@ begin wb_rsp.ack <= wb_in.cyc and wb_in.stb; with wb_in.adr(GPIO_REG_BITS - 1 downto 0) select reg_out <= reg_data when GPIO_REG_DATA_OUT, - reg_in2 when GPIO_REG_DATA_IN, + reg_in1 when GPIO_REG_DATA_IN, reg_dirn when GPIO_REG_DIR, + reg_intr_en when GPIO_REG_INT_EN, + reg_intr_hit when GPIO_REG_INT_STAT, + reg_intr_type when GPIO_REG_INT_TYPE, + reg_intr_both when GPIO_REG_INT_BOTH_EDGE, + reg_intr_level when GPIO_REG_INT_LEVEL, (others => '0') when others; wb_rsp.dat(wb_rsp.dat'left downto NGPIO) <= (others => '0'); wb_rsp.dat(NGPIO - 1 downto 0) <= reg_out; wb_rsp.stall <= '0'; regs_rw: process(clk) + variable trig : std_logic_vector(0 to 2); + variable change : std_logic; + variable intr_hit : boolean; begin if rising_edge(clk) then wb_out <= wb_rsp; + for i in NGPIO - 1 downto 0 loop + -- interrupt triggers. reg_in1 is current value + if reg_intr_type(i) = '0' then + -- edge + change := '0' when (reg_in1(i) = reg_in2(i)) else '1'; + trig := change & reg_intr_both(i) & reg_intr_level(i); + case trig is + -- both + when "110" | "111" => intr_hit := true; + -- rising + when "100" => intr_hit := reg_in1(i) = '1'; + -- falling + when "101" => intr_hit := reg_in1(i) = '0'; + when others => intr_hit := false; + end case; + else + -- level + intr_hit := reg_in1(i) = not reg_intr_level(i); + end if; + reg_intr_hit(i) <= '1' when intr_hit and reg_intr_en(i) = '1'; + + end loop; + + -- previous value for interrupt edge detection reg_in2 <= reg_in1; - reg_in1 <= gpio_in; + -- 2 flip flops to cross from async input to sys clock domain + reg_in1 <= reg_in0; + reg_in0 <= gpio_in; + if rst = '1' then reg_data <= (others => '0'); reg_dirn <= (others => '0'); + reg_intr_en <= (others => '0'); + reg_intr_hit <= (others => '0'); + reg_intr_type <= (others => '0'); + reg_intr_both <= (others => '0'); + reg_intr_level <= (others => '0'); wb_out.ack <= '0'; else if wb_in.cyc = '1' and wb_in.stb = '1' and wb_in.we = '1' then @@ -88,6 +145,16 @@ begin reg_data <= reg_data or wb_in.dat(NGPIO - 1 downto 0); when GPIO_REG_DATA_CLR => reg_data <= reg_data and not wb_in.dat(NGPIO - 1 downto 0); + when GPIO_REG_INT_EN => + reg_intr_en <= wb_in.dat(NGPIO - 1 downto 0); + when GPIO_REG_INT_CLR => + reg_intr_hit <= reg_intr_hit and not wb_in.dat(NGPIO - 1 downto 0); + when GPIO_REG_INT_TYPE => + reg_intr_type <= wb_in.dat(NGPIO - 1 downto 0); + when GPIO_REG_INT_BOTH_EDGE => + reg_intr_both <= wb_in.dat(NGPIO - 1 downto 0); + when GPIO_REG_INT_LEVEL => + reg_intr_level <= wb_in.dat(NGPIO - 1 downto 0); when others => end case; end if; @@ -96,4 +163,4 @@ begin end process; end architecture behaviour; - + diff --git a/include/microwatt_soc.h b/include/microwatt_soc.h index 7d68025..6a28b7d 100644 --- a/include/microwatt_soc.h +++ b/include/microwatt_soc.h @@ -14,6 +14,7 @@ #define XICS_ICP_BASE 0xc0004000 /* Interrupt controller */ #define XICS_ICS_BASE 0xc0005000 /* Interrupt controller */ #define SPI_FCTRL_BASE 0xc0006000 /* SPI flash controller registers */ +#define GPIO_BASE 0xc0007000 /* GPIO registers */ #define DRAM_CTRL_BASE 0xc8000000 /* LiteDRAM control registers */ #define LETH_CSR_BASE 0xc8020000 /* LiteEth CSR registers */ #define LETH_SRAM_BASE 0xc8030000 /* LiteEth MMIO space */ @@ -158,5 +159,20 @@ #define SPI_REG_AUTO_CFG_CSTOUT_SHIFT 24 /* CS timeout */ #define SPI_REG_AUTO_CFG_CSTOUT_MASK (0x3f << SPI_REG_AUTO_CFG_CSTOUT_SHIFT) +/* + * Register definitions for GPIO + */ +#define GPIO_REG_DATA_OUT 0x00 +#define GPIO_REG_DATA_IN 0x04 +#define GPIO_REG_DIR 0x08 +#define GPIO_REG_DATA_SET 0x10 +#define GPIO_REG_DATA_CLR 0x14 + +#define GPIO_REG_INT_EN 0x20 +#define GPIO_REG_INT_STAT 0x24 +#define GPIO_REG_INT_CLR 0x30 +#define GPIO_REG_INT_TYPE 0x34 +#define GPIO_REG_INT_BOTH_EDGE 0x38 +#define GPIO_REG_INT_LEVEL 0x3C #endif /* __MICROWATT_SOC_H */