From 30680c6eb396a2bb06928afd69edae9908ac84fb Mon Sep 17 00:00:00 2001 From: patrickmt <40182064+patrickmt@users.noreply.github.com> Date: Wed, 29 Aug 2018 15:07:52 -0400 Subject: Massdrop keyboard support (#3780) * Massdrop SAMD51 Massdrop SAMD51 keyboards initial project upload * Removing relocated files Removing files that were relocated and not deleted from previous location * LED queue fix and cleaning Cleaned some white space or comments. Fix for LED I2C command queue. Cleaned up interrupts. Added debug function for printing numbers to scope through m15 line. * Factory programmed serial usage Ability to use factory programmed serial in hub and keyboard usb descriptors * USB serial number and bugfix Added support for factory programmed serial and usage. Incorporated bootloader's conditional compiling to align project closer. Fixed issue when USB device attempted to send before enabled. General white space and comment cleanup. * Project cleanup Cleaned up project in terms of white space, commented code, and unecessary files. NKRO keyboard is now using correct setreport although KBD was fine to use. Fixed broken linkage to __xprintf for serial debug statements. * Fix for extra keys Fixed possible USB hang on extra keys report set missing * I2C cleanup I2C cleanup and file renames necessary for master branch merge * Boot tracing and clocks cleanup Added optional boot debug trace mode through debug LED codes. General clock code cleanup. * Relocate ARM/Atmel headers Moved ARM/Atmel header folder from drivers to lib and made necessary makefile changes. * Pull request changes Pull request changes * Keymap and compile flag fix Keymap fix for momentary layer. Potential compile flag fix for Travis CI failure. * va_list include fix Fix for va_list compile failure * Include file case fixes Fixes for include files with incorrect case * ctrl and alt67 keyboard readme Added ctrl and alt67 keyboard readme files --- tmk_core/protocol/arm_atsam.mk | 26 + tmk_core/protocol/arm_atsam/adc.c | 99 ++ tmk_core/protocol/arm_atsam/adc.h | 37 + tmk_core/protocol/arm_atsam/arm_atsam_protocol.h | 45 + tmk_core/protocol/arm_atsam/clks.c | 439 +++++++ tmk_core/protocol/arm_atsam/clks.h | 90 ++ tmk_core/protocol/arm_atsam/d51_util.c | 165 +++ tmk_core/protocol/arm_atsam/d51_util.h | 185 +++ tmk_core/protocol/arm_atsam/i2c_master.c | 585 +++++++++ tmk_core/protocol/arm_atsam/i2c_master.h | 108 ++ tmk_core/protocol/arm_atsam/issi3733_driver.h | 201 +++ tmk_core/protocol/arm_atsam/led_matrix.c | 509 +++++++ tmk_core/protocol/arm_atsam/led_matrix.h | 142 ++ tmk_core/protocol/arm_atsam/main_arm_atsam.c | 279 ++++ tmk_core/protocol/arm_atsam/main_arm_atsam.h | 23 + tmk_core/protocol/arm_atsam/md_bootloader.h | 18 + tmk_core/protocol/arm_atsam/spi.c | 90 ++ tmk_core/protocol/arm_atsam/spi.h | 63 + tmk_core/protocol/arm_atsam/startup.c | 548 ++++++++ tmk_core/protocol/arm_atsam/usb/compiler.h | 1177 +++++++++++++++++ tmk_core/protocol/arm_atsam/usb/conf_usb.h | 163 +++ tmk_core/protocol/arm_atsam/usb/main_usb.c | 118 ++ tmk_core/protocol/arm_atsam/usb/spfssf.c | 268 ++++ tmk_core/protocol/arm_atsam/usb/spfssf.h | 57 + tmk_core/protocol/arm_atsam/usb/status_codes.h | 158 +++ tmk_core/protocol/arm_atsam/usb/udc.c | 1164 ++++++++++++++++ tmk_core/protocol/arm_atsam/usb/udc.h | 260 ++++ tmk_core/protocol/arm_atsam/usb/udc_desc.h | 135 ++ tmk_core/protocol/arm_atsam/usb/udd.h | 396 ++++++ tmk_core/protocol/arm_atsam/usb/udi.h | 133 ++ tmk_core/protocol/arm_atsam/usb/udi_cdc.c | 1384 ++++++++++++++++++++ tmk_core/protocol/arm_atsam/usb/udi_cdc.h | 381 ++++++ tmk_core/protocol/arm_atsam/usb/udi_cdc_conf.h | 72 + tmk_core/protocol/arm_atsam/usb/udi_device_conf.h | 715 ++++++++++ .../protocol/arm_atsam/usb/udi_device_epsize.h | 32 + tmk_core/protocol/arm_atsam/usb/udi_hid.c | 162 +++ tmk_core/protocol/arm_atsam/usb/udi_hid.h | 85 ++ tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c | 845 ++++++++++++ tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.h | 109 ++ tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_conf.h | 60 + tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_desc.c | 179 +++ tmk_core/protocol/arm_atsam/usb/ui.c | 106 ++ tmk_core/protocol/arm_atsam/usb/ui.h | 82 ++ tmk_core/protocol/arm_atsam/usb/usb.c | 1144 ++++++++++++++++ tmk_core/protocol/arm_atsam/usb/usb.h | 492 +++++++ tmk_core/protocol/arm_atsam/usb/usb2422.c | 412 ++++++ tmk_core/protocol/arm_atsam/usb/usb2422.h | 405 ++++++ tmk_core/protocol/arm_atsam/usb/usb_atmel.h | 190 +++ tmk_core/protocol/arm_atsam/usb/usb_device_udd.c | 1097 ++++++++++++++++ tmk_core/protocol/arm_atsam/usb/usb_main.h | 97 ++ tmk_core/protocol/arm_atsam/usb/usb_protocol.h | 498 +++++++ tmk_core/protocol/arm_atsam/usb/usb_protocol_cdc.h | 193 +++ tmk_core/protocol/arm_atsam/usb/usb_protocol_hid.h | 319 +++++ tmk_core/protocol/arm_atsam/usb/usb_util.c | 59 + tmk_core/protocol/arm_atsam/usb/usb_util.h | 10 + tmk_core/protocol/arm_atsam/wait_api.h | 8 + 56 files changed, 16817 insertions(+) create mode 100644 tmk_core/protocol/arm_atsam.mk create mode 100644 tmk_core/protocol/arm_atsam/adc.c create mode 100644 tmk_core/protocol/arm_atsam/adc.h create mode 100644 tmk_core/protocol/arm_atsam/arm_atsam_protocol.h create mode 100644 tmk_core/protocol/arm_atsam/clks.c create mode 100644 tmk_core/protocol/arm_atsam/clks.h create mode 100644 tmk_core/protocol/arm_atsam/d51_util.c create mode 100644 tmk_core/protocol/arm_atsam/d51_util.h create mode 100644 tmk_core/protocol/arm_atsam/i2c_master.c create mode 100644 tmk_core/protocol/arm_atsam/i2c_master.h create mode 100644 tmk_core/protocol/arm_atsam/issi3733_driver.h create mode 100644 tmk_core/protocol/arm_atsam/led_matrix.c create mode 100644 tmk_core/protocol/arm_atsam/led_matrix.h create mode 100644 tmk_core/protocol/arm_atsam/main_arm_atsam.c create mode 100644 tmk_core/protocol/arm_atsam/main_arm_atsam.h create mode 100644 tmk_core/protocol/arm_atsam/md_bootloader.h create mode 100644 tmk_core/protocol/arm_atsam/spi.c create mode 100644 tmk_core/protocol/arm_atsam/spi.h create mode 100644 tmk_core/protocol/arm_atsam/startup.c create mode 100644 tmk_core/protocol/arm_atsam/usb/compiler.h create mode 100644 tmk_core/protocol/arm_atsam/usb/conf_usb.h create mode 100644 tmk_core/protocol/arm_atsam/usb/main_usb.c create mode 100644 tmk_core/protocol/arm_atsam/usb/spfssf.c create mode 100644 tmk_core/protocol/arm_atsam/usb/spfssf.h create mode 100644 tmk_core/protocol/arm_atsam/usb/status_codes.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udc.c create mode 100644 tmk_core/protocol/arm_atsam/usb/udc.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udc_desc.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udd.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_cdc.c create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_cdc.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_cdc_conf.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_device_conf.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_device_epsize.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_hid.c create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_hid.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_conf.h create mode 100644 tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_desc.c create mode 100644 tmk_core/protocol/arm_atsam/usb/ui.c create mode 100644 tmk_core/protocol/arm_atsam/usb/ui.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb.c create mode 100644 tmk_core/protocol/arm_atsam/usb/usb.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb2422.c create mode 100644 tmk_core/protocol/arm_atsam/usb/usb2422.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_atmel.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_device_udd.c create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_main.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_protocol.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_protocol_cdc.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_protocol_hid.h create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_util.c create mode 100644 tmk_core/protocol/arm_atsam/usb/usb_util.h create mode 100644 tmk_core/protocol/arm_atsam/wait_api.h (limited to 'tmk_core/protocol') diff --git a/tmk_core/protocol/arm_atsam.mk b/tmk_core/protocol/arm_atsam.mk new file mode 100644 index 0000000000..d535b64cd7 --- /dev/null +++ b/tmk_core/protocol/arm_atsam.mk @@ -0,0 +1,26 @@ +ARM_ATSAM_DIR = protocol/arm_atsam + +SRC += $(ARM_ATSAM_DIR)/adc.c +SRC += $(ARM_ATSAM_DIR)/clks.c +SRC += $(ARM_ATSAM_DIR)/d51_util.c +SRC += $(ARM_ATSAM_DIR)/i2c_master.c +SRC += $(ARM_ATSAM_DIR)/led_matrix.c +SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c +SRC += $(ARM_ATSAM_DIR)/spi.c +SRC += $(ARM_ATSAM_DIR)/startup.c + +SRC += $(ARM_ATSAM_DIR)/usb/main_usb.c +SRC += $(ARM_ATSAM_DIR)/usb/spfssf.c +SRC += $(ARM_ATSAM_DIR)/usb/udc.c +SRC += $(ARM_ATSAM_DIR)/usb/udi_cdc.c +SRC += $(ARM_ATSAM_DIR)/usb/udi_hid.c +SRC += $(ARM_ATSAM_DIR)/usb/udi_hid_kbd.c +SRC += $(ARM_ATSAM_DIR)/usb/udi_hid_kbd_desc.c +SRC += $(ARM_ATSAM_DIR)/usb/ui.c +SRC += $(ARM_ATSAM_DIR)/usb/usb2422.c +SRC += $(ARM_ATSAM_DIR)/usb/usb.c +SRC += $(ARM_ATSAM_DIR)/usb/usb_device_udd.c +SRC += $(ARM_ATSAM_DIR)/usb/usb_util.c + +# Search Path +VPATH += $(TMK_DIR)/$(ARM_ATSAM_DIR) diff --git a/tmk_core/protocol/arm_atsam/adc.c b/tmk_core/protocol/arm_atsam/adc.c new file mode 100644 index 0000000000..ab77f92402 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/adc.c @@ -0,0 +1,99 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "arm_atsam_protocol.h" + +uint16_t v_5v; +uint16_t v_5v_avg; +uint16_t v_con_1; +uint16_t v_con_2; +uint16_t v_con_1_boot; +uint16_t v_con_2_boot; + +void ADC0_clock_init(void) +{ + DBGC(DC_ADC0_CLOCK_INIT_BEGIN); + + MCLK->APBDMASK.bit.ADC0_ = 1; //ADC0 Clock Enable + + GCLK->PCHCTRL[ADC0_GCLK_ID].bit.GEN = GEN_OSC0; //Select generator clock + GCLK->PCHCTRL[ADC0_GCLK_ID].bit.CHEN = 1; //Enable peripheral clock + + DBGC(DC_ADC0_CLOCK_INIT_COMPLETE); +} + +void ADC0_init(void) +{ + DBGC(DC_ADC0_INIT_BEGIN); + + //MCU + PORT->Group[1].DIRCLR.reg = 1 << 0; //PB00 as input 5V + PORT->Group[1].DIRCLR.reg = 1 << 1; //PB01 as input CON2 + PORT->Group[1].DIRCLR.reg = 1 << 2; //PB02 as input CON1 + PORT->Group[1].PMUX[0].bit.PMUXE = 1; //PB00 mux select B ADC 5V + PORT->Group[1].PMUX[0].bit.PMUXO = 1; //PB01 mux select B ADC CON2 + PORT->Group[1].PMUX[1].bit.PMUXE = 1; //PB02 mux select B ADC CON1 + PORT->Group[1].PINCFG[0].bit.PMUXEN = 1; //PB01 mux ADC Enable 5V + PORT->Group[1].PINCFG[1].bit.PMUXEN = 1; //PB01 mux ADC Enable CON2 + PORT->Group[1].PINCFG[2].bit.PMUXEN = 1; //PB02 mux ADC Enable CON1 + + //ADC + ADC0->CTRLA.bit.SWRST = 1; + while (ADC0->SYNCBUSY.bit.SWRST) { DBGC(DC_ADC0_SWRST_SYNCING_1); } + while (ADC0->CTRLA.bit.SWRST) { DBGC(DC_ADC0_SWRST_SYNCING_2); } + + //Clock divide + ADC0->CTRLA.bit.PRESCALER = ADC_CTRLA_PRESCALER_DIV2_Val; + + //Averaging + ADC0->AVGCTRL.bit.SAMPLENUM = ADC_AVGCTRL_SAMPLENUM_4_Val; + while (ADC0->SYNCBUSY.bit.AVGCTRL) { DBGC(DC_ADC0_AVGCTRL_SYNCING_1); } + if (ADC0->AVGCTRL.bit.SAMPLENUM == ADC_AVGCTRL_SAMPLENUM_1_Val) ADC0->AVGCTRL.bit.ADJRES = 0; + else if (ADC0->AVGCTRL.bit.SAMPLENUM == ADC_AVGCTRL_SAMPLENUM_2_Val) ADC0->AVGCTRL.bit.ADJRES = 1; + else if (ADC0->AVGCTRL.bit.SAMPLENUM == ADC_AVGCTRL_SAMPLENUM_4_Val) ADC0->AVGCTRL.bit.ADJRES = 2; + else if (ADC0->AVGCTRL.bit.SAMPLENUM == ADC_AVGCTRL_SAMPLENUM_8_Val) ADC0->AVGCTRL.bit.ADJRES = 3; + else ADC0->AVGCTRL.bit.ADJRES = 4; + while (ADC0->SYNCBUSY.bit.AVGCTRL) { DBGC(DC_ADC0_AVGCTRL_SYNCING_2); } + + //Settling + ADC0->SAMPCTRL.bit.SAMPLEN = 45; //Sampling Time Length: 1-63, 1 ADC CLK per + while (ADC0->SYNCBUSY.bit.SAMPCTRL) { DBGC(DC_ADC0_SAMPCTRL_SYNCING_1); } + + //Load factory calibration data + ADC0->CALIB.bit.BIASCOMP = (ADC0_FUSES_BIASCOMP_ADDR >> ADC0_FUSES_BIASCOMP_Pos) & ADC0_FUSES_BIASCOMP_Msk; + ADC0->CALIB.bit.BIASR2R = (ADC0_FUSES_BIASR2R_ADDR >> ADC0_FUSES_BIASR2R_Pos) & ADC0_FUSES_BIASR2R_Msk; + ADC0->CALIB.bit.BIASREFBUF = (ADC0_FUSES_BIASREFBUF_ADDR >> ADC0_FUSES_BIASREFBUF_Pos) & ADC0_FUSES_BIASREFBUF_Msk; + + //Enable + ADC0->CTRLA.bit.ENABLE = 1; + while (ADC0->SYNCBUSY.bit.ENABLE) { DBGC(DC_ADC0_ENABLE_SYNCING_1); } + + DBGC(DC_ADC0_INIT_COMPLETE); +} + +uint16_t adc_get(uint8_t muxpos) +{ + ADC0->INPUTCTRL.bit.MUXPOS = muxpos; + while (ADC0->SYNCBUSY.bit.INPUTCTRL) {} + + ADC0->SWTRIG.bit.START = 1; + while (ADC0->SYNCBUSY.bit.SWTRIG) {} + while (!ADC0->INTFLAG.bit.RESRDY) {} + + return ADC0->RESULT.reg; +} + diff --git a/tmk_core/protocol/arm_atsam/adc.h b/tmk_core/protocol/arm_atsam/adc.h new file mode 100644 index 0000000000..5a90ece3fe --- /dev/null +++ b/tmk_core/protocol/arm_atsam/adc.h @@ -0,0 +1,37 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef _ADC_H_ +#define _ADC_H_ + +#define ADC_5V_START_LEVEL 2365 + +#define ADC_5V ADC_INPUTCTRL_MUXPOS_AIN12_Val +#define ADC_CON1 ADC_INPUTCTRL_MUXPOS_AIN14_Val +#define ADC_CON2 ADC_INPUTCTRL_MUXPOS_AIN13_Val + +extern uint16_t v_5v; +extern uint16_t v_5v_avg; +extern uint16_t v_con_1; +extern uint16_t v_con_2; +extern uint16_t v_con_1_boot; +extern uint16_t v_con_2_boot; + +void ADC0_clock_init(void); +void ADC0_init(void); + +#endif //_ADC_H_ diff --git a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h new file mode 100644 index 0000000000..be73beccd7 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h @@ -0,0 +1,45 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef _ARM_ATSAM_PROTOCOL_H_ +#define _ARM_ATSAM_PROTOCOL_H_ + +#include "samd51j18a.h" +#include "md_bootloader.h" + +#include "d51_util.h" +#include "clks.h" +#include "adc.h" +#include "i2c_master.h" +#include "spi.h" + +#include "./usb/usb2422.h" + +#ifndef MD_BOOTLOADER + +#include "main_arm_atsam.h" +#include "led_matrix.h" +#include "issi3733_driver.h" +#include "./usb/compiler.h" +#include "./usb/udc.h" +#include "./usb/spfssf.h" +#include "./usb/udi_cdc.h" + +#endif //MD_BOOTLOADER + +#endif //_ARM_ATSAM_PROTOCOL_H_ + diff --git a/tmk_core/protocol/arm_atsam/clks.c b/tmk_core/protocol/arm_atsam/clks.c new file mode 100644 index 0000000000..8768d0a99e --- /dev/null +++ b/tmk_core/protocol/arm_atsam/clks.c @@ -0,0 +1,439 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "arm_atsam_protocol.h" + +#include + +volatile clk_t system_clks; +volatile uint64_t ms_clk; + +volatile uint8_t us_delay_done; + +const uint32_t sercom_apbbase[] = {(uint32_t)SERCOM0,(uint32_t)SERCOM1,(uint32_t)SERCOM2,(uint32_t)SERCOM3,(uint32_t)SERCOM4,(uint32_t)SERCOM5}; +const uint8_t sercom_pchan[] = {7, 8, 23, 24, 34, 35}; + +#define USE_DPLL_IND 0 +#define USE_DPLL_DEF GCLK_SOURCE_DPLL0 + +void CLK_oscctrl_init(void) +{ + Oscctrl *posctrl = OSCCTRL; + Gclk *pgclk = GCLK; + + DBGC(DC_CLK_OSC_INIT_BEGIN); + + //default setup on por + system_clks.freq_dfll = FREQ_DFLL_DEFAULT; + system_clks.freq_gclk[0] = system_clks.freq_dfll; + + //configure and startup 16MHz xosc0 + posctrl->XOSCCTRL[0].bit.ENABLE = 0; + posctrl->XOSCCTRL[0].bit.STARTUP = 0xD; + posctrl->XOSCCTRL[0].bit.ENALC = 1; + posctrl->XOSCCTRL[0].bit.IMULT = 5; + posctrl->XOSCCTRL[0].bit.IPTAT = 3; + posctrl->XOSCCTRL[0].bit.ONDEMAND = 0; + posctrl->XOSCCTRL[0].bit.XTALEN = 1; + posctrl->XOSCCTRL[0].bit.ENABLE = 1; + while (posctrl->STATUS.bit.XOSCRDY0 == 0) { DBGC(DC_CLK_OSC_INIT_XOSC0_SYNC); } + system_clks.freq_xosc0 = FREQ_XOSC0; + + //configure and startup DPLL + posctrl->Dpll[USE_DPLL_IND].DPLLCTRLA.bit.ENABLE = 0; + while (posctrl->Dpll[USE_DPLL_IND].DPLLSYNCBUSY.bit.ENABLE) { DBGC(DC_CLK_OSC_INIT_DPLL_SYNC_DISABLE); } + posctrl->Dpll[USE_DPLL_IND].DPLLCTRLB.bit.REFCLK = 2; //select XOSC0 (16MHz) + posctrl->Dpll[USE_DPLL_IND].DPLLCTRLB.bit.DIV = 7; //16 MHz / (2 * (7 + 1)) = 1 MHz + posctrl->Dpll[USE_DPLL_IND].DPLLRATIO.bit.LDR = PLL_RATIO; //1 MHz * (PLL_RATIO(47) + 1) = 48MHz + while (posctrl->Dpll[USE_DPLL_IND].DPLLSYNCBUSY.bit.DPLLRATIO) { DBGC(DC_CLK_OSC_INIT_DPLL_SYNC_RATIO); } + posctrl->Dpll[USE_DPLL_IND].DPLLCTRLA.bit.ONDEMAND = 0; + posctrl->Dpll[USE_DPLL_IND].DPLLCTRLA.bit.ENABLE = 1; + while (posctrl->Dpll[USE_DPLL_IND].DPLLSYNCBUSY.bit.ENABLE) { DBGC(DC_CLK_OSC_INIT_DPLL_SYNC_ENABLE); } + while (posctrl->Dpll[USE_DPLL_IND].DPLLSTATUS.bit.LOCK == 0) { DBGC(DC_CLK_OSC_INIT_DPLL_WAIT_LOCK); } + while (posctrl->Dpll[USE_DPLL_IND].DPLLSTATUS.bit.CLKRDY == 0) { DBGC(DC_CLK_OSC_INIT_DPLL_WAIT_CLKRDY); } + system_clks.freq_dpll[0] = (system_clks.freq_xosc0 / 2 / (posctrl->Dpll[USE_DPLL_IND].DPLLCTRLB.bit.DIV + 1)) * (posctrl->Dpll[USE_DPLL_IND].DPLLRATIO.bit.LDR + 1); + + //change gclk0 to DPLL + pgclk->GENCTRL[GEN_DPLL0].bit.SRC = USE_DPLL_DEF; + while (pgclk->SYNCBUSY.bit.GENCTRL0) { DBGC(DC_CLK_OSC_INIT_GCLK_SYNC_GENCTRL0); } + + system_clks.freq_gclk[0] = system_clks.freq_dpll[0]; + + DBGC(DC_CLK_OSC_INIT_COMPLETE); +} + +//configure for 1MHz (1 usec timebase) +//call CLK_set_gclk_freq(GEN_TC45, FREQ_TC45_DEFAULT); +uint32_t CLK_set_gclk_freq(uint8_t gclkn, uint32_t freq) +{ + Gclk *pgclk = GCLK; + + DBGC(DC_CLK_SET_GCLK_FREQ_BEGIN); + + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_SET_GCLK_FREQ_SYNC_1); } + pgclk->GENCTRL[gclkn].bit.SRC = USE_DPLL_DEF; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_SET_GCLK_FREQ_SYNC_2); } + pgclk->GENCTRL[gclkn].bit.DIV = (uint8_t)(system_clks.freq_dpll[0] / freq); + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_SET_GCLK_FREQ_SYNC_3); } + pgclk->GENCTRL[gclkn].bit.DIVSEL = 0; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_SET_GCLK_FREQ_SYNC_4); } + pgclk->GENCTRL[gclkn].bit.GENEN = 1; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_SET_GCLK_FREQ_SYNC_5); } + system_clks.freq_gclk[gclkn] = system_clks.freq_dpll[0] / pgclk->GENCTRL[gclkn].bit.DIV; + + DBGC(DC_CLK_SET_GCLK_FREQ_COMPLETE); + + return system_clks.freq_gclk[gclkn]; +} + +void CLK_init_osc(void) +{ + uint8_t gclkn = GEN_OSC0; + Gclk *pgclk = GCLK; + + DBGC(DC_CLK_INIT_OSC_BEGIN); + + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_INIT_OSC_SYNC_1); } + pgclk->GENCTRL[gclkn].bit.SRC = GCLK_SOURCE_XOSC0; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_INIT_OSC_SYNC_2); } + pgclk->GENCTRL[gclkn].bit.DIV = 1; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_INIT_OSC_SYNC_3); } + pgclk->GENCTRL[gclkn].bit.DIVSEL = 0; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_INIT_OSC_SYNC_4); } + pgclk->GENCTRL[gclkn].bit.GENEN = 1; + while (pgclk->SYNCBUSY.vec.GENCTRL) { DBGC(DC_CLK_INIT_OSC_SYNC_5); } + system_clks.freq_gclk[gclkn] = system_clks.freq_xosc0; + + DBGC(DC_CLK_INIT_OSC_COMPLETE); +} + +void CLK_reset_time(void) +{ + Tc *ptc4 = TC4; + Tc *ptc0 = TC0; + + ms_clk = 0; + + DBGC(DC_CLK_RESET_TIME_BEGIN); + + //stop counters + ptc4->COUNT16.CTRLA.bit.ENABLE = 0; + while (ptc4->COUNT16.SYNCBUSY.bit.ENABLE) {} + ptc0->COUNT32.CTRLA.bit.ENABLE = 0; + while (ptc0->COUNT32.SYNCBUSY.bit.ENABLE) {} + //zero counters + ptc4->COUNT16.COUNT.reg = 0; + while (ptc4->COUNT16.SYNCBUSY.bit.COUNT) {} + ptc0->COUNT32.COUNT.reg = 0; + while (ptc0->COUNT32.SYNCBUSY.bit.COUNT) {} + //start counters + ptc0->COUNT32.CTRLA.bit.ENABLE = 1; + while (ptc0->COUNT32.SYNCBUSY.bit.ENABLE) {} + ptc4->COUNT16.CTRLA.bit.ENABLE = 1; + while (ptc4->COUNT16.SYNCBUSY.bit.ENABLE) {} + + DBGC(DC_CLK_RESET_TIME_COMPLETE); +} + +void TC4_Handler() +{ + if (TC4->COUNT16.INTFLAG.bit.MC0) + { + TC4->COUNT16.INTFLAG.reg = TC_INTENCLR_MC0; + ms_clk++; + } +} + +void TC5_Handler() +{ + if (TC5->COUNT16.INTFLAG.bit.MC0) + { + TC5->COUNT16.INTFLAG.reg = TC_INTENCLR_MC0; + us_delay_done = 1; + TC5->COUNT16.CTRLA.bit.ENABLE = 0; + while (TC5->COUNT16.SYNCBUSY.bit.ENABLE) {} + } +} + +uint32_t CLK_enable_timebase(void) +{ + Gclk *pgclk = GCLK; + Mclk *pmclk = MCLK; + Tc *ptc4 = TC4; + Tc *ptc5 = TC5; + Tc *ptc0 = TC0; + Evsys *pevsys = EVSYS; + + DBGC(DC_CLK_ENABLE_TIMEBASE_BEGIN); + + //gclk2 highspeed time base + CLK_set_gclk_freq(GEN_TC45, FREQ_TC45_DEFAULT); + CLK_init_osc(); + + //unmask TC4, sourcegclk2 to TC4 + pmclk->APBCMASK.bit.TC4_ = 1; + pgclk->PCHCTRL[TC4_GCLK_ID].bit.GEN = GEN_TC45; + pgclk->PCHCTRL[TC4_GCLK_ID].bit.CHEN = 1; + + //unmask TC5 sourcegclk2 to TC5 + pmclk->APBCMASK.bit.TC5_ = 1; + pgclk->PCHCTRL[TC5_GCLK_ID].bit.GEN = GEN_TC45; + pgclk->PCHCTRL[TC5_GCLK_ID].bit.CHEN = 1; + + //configure TC4 + DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_BEGIN); + ptc4->COUNT16.CTRLA.bit.ENABLE = 0; + while (ptc4->COUNT16.SYNCBUSY.bit.ENABLE) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_DISABLE); } + ptc4->COUNT16.CTRLA.bit.SWRST = 1; + while (ptc4->COUNT16.SYNCBUSY.bit.SWRST) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_SWRST_1); } + while (ptc4->COUNT16.CTRLA.bit.SWRST) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_SWRST_2); } + + //CTRLA defaults + //CTRLB as default, counting up + ptc4->COUNT16.CTRLBCLR.reg = 5; + while (ptc4->COUNT16.SYNCBUSY.bit.CTRLB) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_CLTRB); } + ptc4->COUNT16.CC[0].reg = 999; + while (ptc4->COUNT16.SYNCBUSY.bit.CC0) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_CC0); } + //ptc4->COUNT16.DBGCTRL.bit.DBGRUN = 1; + + //wave mode + ptc4->COUNT16.WAVE.bit.WAVEGEN = 1; //MFRQ match frequency mode, toggle each CC match + //generate event for next stage + ptc4->COUNT16.EVCTRL.bit.MCEO0 = 1; + + NVIC_EnableIRQ(TC4_IRQn); + ptc4->COUNT16.INTENSET.bit.MC0 = 1; + + DBGC(DC_CLK_ENABLE_TIMEBASE_TC4_COMPLETE); + + //configure TC5 + DBGC(DC_CLK_ENABLE_TIMEBASE_TC5_BEGIN); + ptc5->COUNT16.CTRLA.bit.ENABLE = 0; + while (ptc5->COUNT16.SYNCBUSY.bit.ENABLE) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_DISABLE); } + ptc5->COUNT16.CTRLA.bit.SWRST = 1; + while (ptc5->COUNT16.SYNCBUSY.bit.SWRST) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_SWRST_1); } + while (ptc5->COUNT16.CTRLA.bit.SWRST) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_SWRST_2); } + + //CTRLA defaults + //CTRLB as default, counting up + ptc5->COUNT16.CTRLBCLR.reg = 5; + while (ptc5->COUNT16.SYNCBUSY.bit.CTRLB) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_CLTRB); } + //ptc5->COUNT16.DBGCTRL.bit.DBGRUN = 1; + + //wave mode + ptc5->COUNT16.WAVE.bit.WAVEGEN = 1; //MFRQ match frequency mode, toggle each CC match + //generate event for next stage + ptc5->COUNT16.EVCTRL.bit.MCEO0 = 1; + + NVIC_EnableIRQ(TC5_IRQn); + ptc5->COUNT16.INTENSET.bit.MC0 = 1; + + DBGC(DC_CLK_ENABLE_TIMEBASE_TC5_COMPLETE); + + //unmask TC0,1, sourcegclk2 to TC0,1 + pmclk->APBAMASK.bit.TC0_ = 1; + pgclk->PCHCTRL[TC0_GCLK_ID].bit.GEN = GEN_TC45; + pgclk->PCHCTRL[TC0_GCLK_ID].bit.CHEN = 1; + + pmclk->APBAMASK.bit.TC1_ = 1; + pgclk->PCHCTRL[TC1_GCLK_ID].bit.GEN = GEN_TC45; + pgclk->PCHCTRL[TC1_GCLK_ID].bit.CHEN = 1; + + //configure TC0 + DBGC(DC_CLK_ENABLE_TIMEBASE_TC0_BEGIN); + ptc0->COUNT32.CTRLA.bit.ENABLE = 0; + while (ptc0->COUNT32.SYNCBUSY.bit.ENABLE) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC0_SYNC_DISABLE); } + ptc0->COUNT32.CTRLA.bit.SWRST = 1; + while (ptc0->COUNT32.SYNCBUSY.bit.SWRST) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC0_SYNC_SWRST_1); } + while (ptc0->COUNT32.CTRLA.bit.SWRST) { DBGC(DC_CLK_ENABLE_TIMEBASE_TC0_SYNC_SWRST_2); } + //CTRLA as default + ptc0->COUNT32.CTRLA.bit.MODE = 2; //32 bit mode + ptc0->COUNT32.EVCTRL.bit.TCEI = 1; //enable incoming events + ptc0->COUNT32.EVCTRL.bit.EVACT = 2 ; //count events + + DBGC(DC_CLK_ENABLE_TIMEBASE_TC0_COMPLETE); + + DBGC(DC_CLK_ENABLE_TIMEBASE_EVSYS_BEGIN); + + //configure event system + pmclk->APBBMASK.bit.EVSYS_ = 1; + pgclk->PCHCTRL[EVSYS_GCLK_ID_0].bit.GEN = GEN_TC45; + pgclk->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN = 1; + pevsys->USER[44].reg = EVSYS_ID_USER_PORT_EV_0; //TC0 will get event channel 0 + pevsys->Channel[0].CHANNEL.bit.EDGSEL = EVSYS_CHANNEL_EDGSEL_RISING_EDGE_Val; //Rising edge + pevsys->Channel[0].CHANNEL.bit.PATH = EVSYS_CHANNEL_PATH_SYNCHRONOUS_Val; //Synchronous + pevsys->Channel[0].CHANNEL.bit.EVGEN = EVSYS_ID_GEN_TC4_MCX_0; //TC4 MC0 + + DBGC(DC_CLK_ENABLE_TIMEBASE_EVSYS_COMPLETE); + + CLK_reset_time(); + + ADC0_clock_init(); + + DBGC(DC_CLK_ENABLE_TIMEBASE_COMPLETE); + + return 0; +} + +uint32_t CLK_get_ms(void) +{ + return ms_clk; +} + +void CLK_delay_us(uint16_t usec) +{ + us_delay_done = 0; + + if (TC5->COUNT16.CTRLA.bit.ENABLE) + { + TC5->COUNT16.CTRLA.bit.ENABLE = 0; + while (TC5->COUNT16.SYNCBUSY.bit.ENABLE) {} + } + + if (usec < 10) usec = 0; + else usec -= 10; + + TC5->COUNT16.CC[0].reg = usec; + while (TC5->COUNT16.SYNCBUSY.bit.CC0) {} + + TC5->COUNT16.CTRLA.bit.ENABLE = 1; + while (TC5->COUNT16.SYNCBUSY.bit.ENABLE) {} + + while (!us_delay_done) {} +} + +void CLK_delay_ms(uint64_t msec) +{ + msec += CLK_get_ms(); + while (msec > CLK_get_ms()) {} +} + +void clk_enable_sercom_apbmask(int sercomn) +{ + Mclk *pmclk = MCLK; + switch (sercomn) + { + case 0: + pmclk->APBAMASK.bit.SERCOM0_ = 1; + break; + case 1: + pmclk->APBAMASK.bit.SERCOM1_ = 1; + break; + case 2: + pmclk->APBBMASK.bit.SERCOM2_ = 1; + break; + case 3: + pmclk->APBBMASK.bit.SERCOM3_ = 1; + break; + default: + break; + } +} + +//call CLK_oscctrl_init first +//call CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT); +uint32_t CLK_set_spi_freq(uint8_t sercomn, uint32_t freq) +{ + DBGC(DC_CLK_SET_SPI_FREQ_BEGIN); + + Gclk *pgclk = GCLK; + Sercom *psercom = (Sercom *)sercom_apbbase[sercomn]; + clk_enable_sercom_apbmask(sercomn); + + //all gclk0 for now + pgclk->PCHCTRL[sercom_pchan[sercomn]].bit.GEN = 0; + pgclk->PCHCTRL[sercom_pchan[sercomn]].bit.CHEN = 1; + + psercom->I2CM.CTRLA.bit.SWRST = 1; + while (psercom->I2CM.SYNCBUSY.bit.SWRST) {} + while (psercom->I2CM.CTRLA.bit.SWRST) {} + + psercom->SPI.BAUD.reg = (uint8_t) (system_clks.freq_gclk[0]/2/freq-1); + system_clks.freq_spi = system_clks.freq_gclk[0]/2/(psercom->SPI.BAUD.reg+1); + system_clks.freq_sercom[sercomn] = system_clks.freq_spi; + + DBGC(DC_CLK_SET_SPI_FREQ_COMPLETE); + + return system_clks.freq_spi; +} + +//call CLK_oscctrl_init first +//call CLK_set_i2c0_freq(CHAN_SERCOM_I2C0, FREQ_I2C0_DEFAULT); +uint32_t CLK_set_i2c0_freq(uint8_t sercomn, uint32_t freq) +{ + DBGC(DC_CLK_SET_I2C0_FREQ_BEGIN); + + Gclk *pgclk = GCLK; + Sercom *psercom = (Sercom *)sercom_apbbase[sercomn]; + clk_enable_sercom_apbmask(sercomn); + + //all gclk0 for now + pgclk->PCHCTRL[sercom_pchan[sercomn]].bit.GEN = 0; + pgclk->PCHCTRL[sercom_pchan[sercomn]].bit.CHEN = 1; + + psercom->I2CM.CTRLA.bit.SWRST = 1; + while (psercom->I2CM.SYNCBUSY.bit.SWRST) {} + while (psercom->I2CM.CTRLA.bit.SWRST) {} + + psercom->I2CM.BAUD.bit.BAUD = (uint8_t) (system_clks.freq_gclk[0]/2/freq-1); + system_clks.freq_i2c0 = system_clks.freq_gclk[0]/2/(psercom->I2CM.BAUD.bit.BAUD+1); + system_clks.freq_sercom[sercomn] = system_clks.freq_i2c0; + + DBGC(DC_CLK_SET_I2C0_FREQ_COMPLETE); + + return system_clks.freq_i2c0; +} + +//call CLK_oscctrl_init first +//call CLK_set_i2c1_freq(CHAN_SERCOM_I2C1, FREQ_I2C1_DEFAULT); +uint32_t CLK_set_i2c1_freq(uint8_t sercomn, uint32_t freq) +{ + DBGC(DC_CLK_SET_I2C1_FREQ_BEGIN); + + Gclk *pgclk = GCLK; + Sercom *psercom = (Sercom *)sercom_apbbase[sercomn]; + clk_enable_sercom_apbmask(sercomn); + + //all gclk0 for now + pgclk->PCHCTRL[sercom_pchan[sercomn]].bit.GEN = 0; + pgclk->PCHCTRL[sercom_pchan[sercomn]].bit.CHEN = 1; + + psercom->I2CM.CTRLA.bit.SWRST = 1; + while (psercom->I2CM.SYNCBUSY.bit.SWRST) {} + while (psercom->I2CM.CTRLA.bit.SWRST) {} + + psercom->I2CM.BAUD.bit.BAUD = (uint8_t) (system_clks.freq_gclk[0]/2/freq-10); + system_clks.freq_i2c1 = system_clks.freq_gclk[0]/2/(psercom->I2CM.BAUD.bit.BAUD+10); + system_clks.freq_sercom[sercomn] = system_clks.freq_i2c1; + + DBGC(DC_CLK_SET_I2C1_FREQ_COMPLETE); + + return system_clks.freq_i2c1; +} + +void CLK_init(void) +{ + DBGC(DC_CLK_INIT_BEGIN); + + memset((void *)&system_clks,0,sizeof(system_clks)); + + CLK_oscctrl_init(); + CLK_enable_timebase(); + + DBGC(DC_CLK_INIT_COMPLETE); +} + diff --git a/tmk_core/protocol/arm_atsam/clks.h b/tmk_core/protocol/arm_atsam/clks.h new file mode 100644 index 0000000000..96819bfdd0 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/clks.h @@ -0,0 +1,90 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef _CLKS_H_ +#define _CLKS_H_ + +#ifndef MD_BOOTLOADER + +//From keyboard +#include "config_led.h" +#include "config.h" + +#endif //MD_BOOTLOADER + +#define PLL_RATIO 47 //mcu frequency ((X+1)MHz) +#define FREQ_DFLL_DEFAULT 48000000 //DFLL frequency / usb clock +#define FREQ_SPI_DEFAULT 1000000 //spi to 595 shift regs +#define FREQ_I2C0_DEFAULT 100000 //i2c to hub +#define FREQ_I2C1_DEFAULT I2C_HZ //i2c to LED drivers +#define FREQ_TC45_DEFAULT 1000000 //1 usec resolution + +//I2C1 Set ~Result PWM Time (2x Drivers) +// 1000000 1090000 +// 900000 1000000 3.82ms +// 800000 860000 +// 700000 750000 +// 600000 630000 +// 580000 615000 6.08ms +// 500000 522000 + +#define FREQ_XOSC0 16000000 + +#define CHAN_SERCOM_SPI 2 //shift regs +#define CHAN_SERCOM_I2C0 0 //hub +#define CHAN_SERCOM_I2C1 1 //led drivers +#define CHAN_SERCOM_UART 3 //debug util + +//Generator clock channels +#define GEN_DPLL0 0 +#define GEN_OSC0 1 +#define GEN_TC45 2 + +#define SERCOM_COUNT 5 +#define GCLK_COUNT 12 + +typedef struct clk_s { + uint32_t freq_dfll; + uint32_t freq_dpll[2]; + uint32_t freq_sercom[SERCOM_COUNT]; + uint32_t freq_gclk[GCLK_COUNT]; + uint32_t freq_xosc0; + uint32_t freq_spi; + uint32_t freq_i2c0; + uint32_t freq_i2c1; + uint32_t freq_uart; + uint32_t freq_adc0; +} clk_t; + +extern volatile clk_t system_clks; +extern volatile uint64_t ms_clk; + +void CLK_oscctrl_init(void); +void CLK_reset_time(void); +uint32_t CLK_set_gclk_freq(uint8_t gclkn, uint32_t freq); +uint32_t CLK_enable_timebase(void); +uint32_t CLK_get_ms(void); +uint64_t CLK_get_us(void); +void CLK_delay_us(uint16_t usec); +void CLK_delay_ms(uint64_t msec); + +uint32_t CLK_set_spi_freq(uint8_t sercomn, uint32_t freq); +uint32_t CLK_set_i2c0_freq(uint8_t sercomn, uint32_t freq); +uint32_t CLK_set_i2c1_freq(uint8_t sercomn, uint32_t freq); +void CLK_init(void); + +#endif // _CLKS_H_ diff --git a/tmk_core/protocol/arm_atsam/d51_util.c b/tmk_core/protocol/arm_atsam/d51_util.c new file mode 100644 index 0000000000..91b58757cf --- /dev/null +++ b/tmk_core/protocol/arm_atsam/d51_util.c @@ -0,0 +1,165 @@ +#include "d51_util.h" + +//Display unsigned 32-bit number through m15 +//Read as follows: 1230 = || ||| |||| | (note always ending toggle) +void m15_print(uint32_t x) +{ + int8_t t; + uint32_t n; + uint32_t p, p2; + + if (x < 10) t = 0; + else if (x < 100) t = 1; + else if (x < 1000) t = 2; + else if (x < 10000) t = 3; + else if (x < 100000) t = 4; + else if (x < 1000000) t = 5; + else if (x < 10000000) t = 6; + else if (x < 100000000) t = 7; + else if (x < 1000000000) t = 8; + else t = 9; + + while (t >= 0) + { + p2 = t; + p = 1; + while (p2--) p *= 10; + n = x / p; + x -= n * p; + while (n > 0) + { + m15_on; + n--; + m15_off; + } + //Will always end with an extra toggle + m15_on; + t--; + m15_off; + } +} + +//Display unsigned 32-bit number through debug led +//Read as follows: 1230 = [*] [* *] [* * *] [**] (note zero is fast double flash) +#define DLED_ONTIME 600000 +#define DLED_PAUSE 1000000 +volatile uint32_t w; +void dled_print(uint32_t x, uint8_t long_pause) +{ + int8_t t; + uint32_t n; + uint32_t p, p2; + + if (x < 10) t = 0; + else if (x < 100) t = 1; + else if (x < 1000) t = 2; + else if (x < 10000) t = 3; + else if (x < 100000) t = 4; + else if (x < 1000000) t = 5; + else if (x < 10000000) t = 6; + else if (x < 100000000) t = 7; + else if (x < 1000000000) t = 8; + else t = 9; + + while (t >= 0) + { + p2 = t; + p = 1; + while (p2--) p *= 10; + n = x / p; + x -= n * p; + if (!n) + { + led_on; + for (w = DLED_ONTIME / 4; w; w--); + led_off; + for (w = DLED_ONTIME / 4; w; w--); + led_on; + for (w = DLED_ONTIME / 4; w; w--); + led_off; + for (w = DLED_ONTIME / 4; w; w--); + n--; + } + else + { + while (n > 0) + { + led_on; + for (w = DLED_ONTIME; w; w--); + led_off; + for (w = DLED_ONTIME / 2; w; w--); + n--; + } + } + + for (w = DLED_PAUSE; w; w--); + t--; + } + + if (long_pause) + { + for (w = DLED_PAUSE * 4; w; w--); + } +} + +#ifdef DEBUG_BOOT_TRACING + +volatile uint32_t debug_code; + +void EIC_15_Handler() +{ + //This is only for non-functional keyboard troubleshooting and should be disabled after boot + //Intention is to lock up the keyboard here with repeating debug led code + while (1) + { + dled_print(debug_code, 1); + } +} + +void debug_code_init(void) +{ + DBGC(DC_UNSET); + + //Configure Ports for EIC on PB31 + PORT->Group[1].DIRCLR.reg = 1 << 31; //Input + PORT->Group[1].OUTSET.reg = 1 << 31; //High + PORT->Group[1].PINCFG[31].bit.INEN = 1; //Input Enable + PORT->Group[1].PINCFG[31].bit.PULLEN = 1; //Pull Enable + PORT->Group[1].PINCFG[31].bit.PMUXEN = 1; //Mux Enable + PORT->Group[1].PMUX[15].bit.PMUXO = 0; //Mux A + + //Enable CLK_EIC_APB + MCLK->APBAMASK.bit.EIC_ = 1; + + //Configure EIC + EIC->CTRLA.bit.SWRST = 1; + while (EIC->SYNCBUSY.bit.SWRST) {} + EIC->ASYNCH.reg = 1 << 15; + EIC->INTENSET.reg = 1 << 15; + EIC->CONFIG[1].bit.SENSE7 = 2; + EIC->CTRLA.bit.ENABLE = 1; + while (EIC->SYNCBUSY.bit.ENABLE) {} + + //Enable EIC IRQ + NVIC_EnableIRQ(EIC_15_IRQn); +} + +void debug_code_disable(void) +{ + //Disable EIC IRQ + NVIC_DisableIRQ(EIC_15_IRQn); + + //Disable EIC + EIC->CTRLA.bit.ENABLE = 0; + while (EIC->SYNCBUSY.bit.ENABLE) {} + + //Disable CLK_EIC_APB + MCLK->APBAMASK.bit.EIC_ = 0; +} + +#else + +void debug_code_init(void) {} +void debug_code_disable(void) {} + +#endif //DEBUG_BOOT_TRACING diff --git a/tmk_core/protocol/arm_atsam/d51_util.h b/tmk_core/protocol/arm_atsam/d51_util.h new file mode 100644 index 0000000000..465889c7cb --- /dev/null +++ b/tmk_core/protocol/arm_atsam/d51_util.h @@ -0,0 +1,185 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef _D51_UTIL_H_ +#define _D51_UTIL_H_ + +#include "samd51j18a.h" + +//TODO: PS: Should bring these ports out to keyboard level configuration + +//Debug LED PA27 +#define led_ena REG_PORT_DIRSET0 = 0x08000000 //PA27 Output +#define led_on REG_PORT_OUTSET0 = 0x08000000 //PA27 High +#define led_off REG_PORT_OUTCLR0 = 0x08000000 //PA27 Low + +//Debug Port PB30 +#define m15_ena REG_PORT_DIRSET1 = 0x40000000 //PB30 Output +#define m15_on REG_PORT_OUTSET1 = 0x40000000 //PB30 High +#define m15_off REG_PORT_OUTCLR1 = 0x40000000 //PB30 Low + +#define m15_loop(M15X) {uint8_t M15L=M15X; while(M15L--){m15_on;CLK_delay_us(1);m15_off;}} + +void m15_print(uint32_t x); +void dled_print(uint32_t x, uint8_t long_pause); + +void debug_code_init(void); +void debug_code_disable(void); + +#ifdef DEBUG_BOOT_TRACING + +#define DBGC(n) debug_code = n + +extern volatile uint32_t debug_code; + +enum debug_code_list { + DC_UNSET = 0, + DC_CLK_INIT_BEGIN, + DC_CLK_INIT_COMPLETE, + DC_CLK_SET_I2C1_FREQ_BEGIN, + DC_CLK_SET_I2C1_FREQ_COMPLETE, + DC_CLK_SET_I2C0_FREQ_BEGIN, + DC_CLK_SET_I2C0_FREQ_COMPLETE, + DC_CLK_SET_SPI_FREQ_BEGIN, + DC_CLK_SET_SPI_FREQ_COMPLETE, + DC_CLK_ENABLE_TIMEBASE_BEGIN, + DC_CLK_ENABLE_TIMEBASE_SYNC_ENABLE, + DC_CLK_ENABLE_TIMEBASE_SYNC_SWRST_1, + DC_CLK_ENABLE_TIMEBASE_SYNC_SWRST_2, + DC_CLK_ENABLE_TIMEBASE_TC4_BEGIN, + DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_DISABLE, + DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_SWRST_1, + DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_SWRST_2, + DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_CLTRB, + DC_CLK_ENABLE_TIMEBASE_TC4_SYNC_CC0, + DC_CLK_ENABLE_TIMEBASE_TC4_COMPLETE, + DC_CLK_ENABLE_TIMEBASE_TC5_BEGIN, + DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_DISABLE, + DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_SWRST_1, + DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_SWRST_2, + DC_CLK_ENABLE_TIMEBASE_TC5_SYNC_CLTRB, + DC_CLK_ENABLE_TIMEBASE_TC5_COMPLETE, + DC_CLK_ENABLE_TIMEBASE_TC0_BEGIN, + DC_CLK_ENABLE_TIMEBASE_TC0_SYNC_DISABLE, + DC_CLK_ENABLE_TIMEBASE_TC0_SYNC_SWRST_1, + DC_CLK_ENABLE_TIMEBASE_TC0_SYNC_SWRST_2, + DC_CLK_ENABLE_TIMEBASE_TC0_COMPLETE, + DC_CLK_ENABLE_TIMEBASE_EVSYS_BEGIN, + DC_CLK_ENABLE_TIMEBASE_EVSYS_COMPLETE, + DC_CLK_ENABLE_TIMEBASE_COMPLETE, + DC_CLK_SET_GCLK_FREQ_BEGIN, + DC_CLK_SET_GCLK_FREQ_SYNC_1, + DC_CLK_SET_GCLK_FREQ_SYNC_2, + DC_CLK_SET_GCLK_FREQ_SYNC_3, + DC_CLK_SET_GCLK_FREQ_SYNC_4, + DC_CLK_SET_GCLK_FREQ_SYNC_5, + DC_CLK_SET_GCLK_FREQ_COMPLETE, + DC_CLK_INIT_OSC_BEGIN, + DC_CLK_INIT_OSC_SYNC_1, + DC_CLK_INIT_OSC_SYNC_2, + DC_CLK_INIT_OSC_SYNC_3, + DC_CLK_INIT_OSC_SYNC_4, + DC_CLK_INIT_OSC_SYNC_5, + DC_CLK_INIT_OSC_COMPLETE, + DC_CLK_RESET_TIME_BEGIN, + DC_CLK_RESET_TIME_COMPLETE, + DC_CLK_OSC_INIT_BEGIN, + DC_CLK_OSC_INIT_XOSC0_SYNC, + DC_CLK_OSC_INIT_DPLL_SYNC_DISABLE, + DC_CLK_OSC_INIT_DPLL_SYNC_RATIO, + DC_CLK_OSC_INIT_DPLL_SYNC_ENABLE, + DC_CLK_OSC_INIT_DPLL_WAIT_LOCK, + DC_CLK_OSC_INIT_DPLL_WAIT_CLKRDY, + DC_CLK_OSC_INIT_GCLK_SYNC_GENCTRL0, + DC_CLK_OSC_INIT_COMPLETE, + DC_SPI_INIT_BEGIN, + DC_SPI_WRITE_DRE, + DC_SPI_WRITE_TXC_1, + DC_SPI_WRITE_TXC_2, + DC_SPI_SYNC_ENABLING, + DC_SPI_INIT_COMPLETE, + DC_PORT_DETECT_INIT_BEGIN, + DC_PORT_DETECT_INIT_FAILED, + DC_PORT_DETECT_INIT_COMPLETE, + DC_USB_RESET_BEGIN, + DC_USB_RESET_COMPLETE, + DC_USB_SET_HOST_BY_VOLTAGE_BEGIN, + DC_USB_SET_HOST_5V_LOW_WAITING, + DC_USB_SET_HOST_BY_VOLTAGE_COMPLETE, + DC_USB_CONFIGURE_BEGIN, + DC_USB_CONFIGURE_GET_SERIAL, + DC_USB_CONFIGURE_COMPLETE, + DC_USB_WRITE2422_BLOCK_BEGIN, + DC_USB_WRITE2422_BLOCK_SYNC_SYSOP, + DC_USB_WRITE2422_BLOCK_COMPLETE, + DC_ADC0_CLOCK_INIT_BEGIN, + DC_ADC0_CLOCK_INIT_COMPLETE, + DC_ADC0_INIT_BEGIN, + DC_ADC0_SWRST_SYNCING_1, + DC_ADC0_SWRST_SYNCING_2, + DC_ADC0_AVGCTRL_SYNCING_1, + DC_ADC0_AVGCTRL_SYNCING_2, + DC_ADC0_SAMPCTRL_SYNCING_1, + DC_ADC0_ENABLE_SYNCING_1, + DC_ADC0_INIT_COMPLETE, + DC_I2C0_INIT_BEGIN, + DC_I2C0_INIT_SYNC_ENABLING, + DC_I2C0_INIT_SYNC_SYSOP, + DC_I2C0_INIT_WAIT_IDLE, + DC_I2C0_INIT_COMPLETE, + DC_I2C1_INIT_BEGIN, + DC_I2C1_INIT_SYNC_ENABLING, + DC_I2C1_INIT_SYNC_SYSOP, + DC_I2C1_INIT_WAIT_IDLE, + DC_I2C1_INIT_COMPLETE, + DC_I2C3733_INIT_CONTROL_BEGIN, + DC_I2C3733_INIT_CONTROL_COMPLETE, + DC_I2C3733_INIT_DRIVERS_BEGIN, + DC_I2C3733_INIT_DRIVERS_COMPLETE, + DC_I2C_DMAC_LED_INIT_BEGIN, + DC_I2C_DMAC_LED_INIT_COMPLETE, + DC_I2C3733_CONTROL_SET_BEGIN, + DC_I2C3733_CONTROL_SET_COMPLETE, + DC_LED_MATRIX_INIT_BEGIN, + DC_LED_MATRIX_INIT_COMPLETE, + DC_USB2422_INIT_BEGIN, + DC_USB2422_INIT_WAIT_5V_LOW, + DC_USB2422_INIT_OSC_SYNC_DISABLING, + DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_1, + DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_2, + DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_3, + DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_4, + DC_USB2422_INIT_OSC_SYNC_DFLLMUL, + DC_USB2422_INIT_OSC_SYNC_ENABLING, + DC_USB2422_INIT_USB_SYNC_SWRST, + DC_USB2422_INIT_USB_WAIT_SWRST, + DC_USB2422_INIT_USB_SYNC_ENABLING, + DC_USB2422_INIT_COMPLETE, + DC_MAIN_UDC_START_BEGIN, + DC_MAIN_UDC_START_COMPLETE, + DC_MAIN_CDC_INIT_BEGIN, + DC_MAIN_CDC_INIT_COMPLETE, + /* Never change the order of error codes! Only add codes to end! */ +}; + +#else + +#define DBGC(n) {} + +#endif //DEBUG_BOOT_TRACING + +#endif //_D51_UTIL_H_ diff --git a/tmk_core/protocol/arm_atsam/i2c_master.c b/tmk_core/protocol/arm_atsam/i2c_master.c new file mode 100644 index 0000000000..bbe909e9bb --- /dev/null +++ b/tmk_core/protocol/arm_atsam/i2c_master.c @@ -0,0 +1,585 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "arm_atsam_protocol.h" + +#ifndef MD_BOOTLOADER + +#include + +//From keyboard +#include "config.h" +#include "config_led.h" +#include "matrix.h" + +#define I2C_LED_USE_DMA 1 //Set 1 to use background DMA transfers for leds, Set 0 to use inline software transfers + +static uint8_t i2c_led_q[I2C_Q_SIZE]; //I2C queue circular buffer +static uint8_t i2c_led_q_s; //Start of circular buffer +static uint8_t i2c_led_q_e; //End of circular buffer +static uint8_t i2c_led_q_full; //Queue full counter for reset + +static uint8_t dma_sendbuf[I2C_DMA_MAX_SEND]; //Data being written to I2C + +volatile uint8_t i2c_led_q_running; + +#endif //MD_BOOTLOADER + +void i2c0_init(void) +{ + DBGC(DC_I2C0_INIT_BEGIN); + + CLK_set_i2c0_freq(CHAN_SERCOM_I2C0, FREQ_I2C0_DEFAULT); + + //MCU + PORT->Group[0].PMUX[4].bit.PMUXE = 2; + PORT->Group[0].PMUX[4].bit.PMUXO = 2; + PORT->Group[0].PINCFG[8].bit.PMUXEN = 1; + PORT->Group[0].PINCFG[9].bit.PMUXEN = 1; + + //I2C + //Note: SW Reset handled in CLK_set_i2c0_freq clks.c + + SERCOM0->I2CM.CTRLA.bit.MODE = 5; //Set master mode + + SERCOM0->I2CM.CTRLA.bit.SPEED = 0; //Set to 1 for Fast-mode Plus (FM+) up to 1 MHz + SERCOM0->I2CM.CTRLA.bit.RUNSTDBY = 1; //Enabled + + SERCOM0->I2CM.CTRLA.bit.ENABLE = 1; //Enable the device + while (SERCOM0->I2CM.SYNCBUSY.bit.ENABLE) { DBGC(DC_I2C0_INIT_SYNC_ENABLING); } //Wait for SYNCBUSY.ENABLE to clear + + SERCOM0->I2CM.STATUS.bit.BUSSTATE = 1; //Force into IDLE state + while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) { DBGC(DC_I2C0_INIT_SYNC_SYSOP); } + while (SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1) { DBGC(DC_I2C0_INIT_WAIT_IDLE); } //Wait while not idle + + DBGC(DC_I2C0_INIT_COMPLETE); +} + +uint8_t i2c0_start(uint8_t address) +{ + SERCOM0->I2CM.ADDR.bit.ADDR = address; + while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {} + while (SERCOM0->I2CM.INTFLAG.bit.MB == 0) {} + while (SERCOM0->I2CM.STATUS.bit.RXNACK) {} + + return 1; +} + +uint8_t i2c0_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout) +{ + if (!length) return 0; + + i2c0_start(address); + + while (length) + { + SERCOM0->I2CM.DATA.bit.DATA = *data; + while (SERCOM0->I2CM.INTFLAG.bit.MB == 0) {} + while (SERCOM0->I2CM.STATUS.bit.RXNACK) {} + + data++; + length--; + } + + i2c0_stop(); + + return 1; +} + +void i2c0_stop(void) +{ + if (SERCOM0->I2CM.STATUS.bit.CLKHOLD || SERCOM0->I2CM.INTFLAG.bit.MB == 1 || SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1) + { + SERCOM0->I2CM.CTRLB.bit.CMD = 3; + while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP); + while (SERCOM0->I2CM.STATUS.bit.CLKHOLD); + while (SERCOM0->I2CM.INTFLAG.bit.MB); + while (SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1); + } +} + +#ifndef MD_BOOTLOADER +void i2c1_init(void) +{ + DBGC(DC_I2C1_INIT_BEGIN); + + CLK_set_i2c1_freq(CHAN_SERCOM_I2C1, FREQ_I2C1_DEFAULT); + + /* MCU */ + PORT->Group[0].PMUX[8].bit.PMUXE = 2; + PORT->Group[0].PMUX[8].bit.PMUXO = 2; + PORT->Group[0].PINCFG[16].bit.PMUXEN = 1; + PORT->Group[0].PINCFG[17].bit.PMUXEN = 1; + + /* I2C */ + //Note: SW Reset handled in CLK_set_i2c1_freq clks.c + + SERCOM1->I2CM.CTRLA.bit.MODE = 5; //MODE: Set master mode (No sync) + SERCOM1->I2CM.CTRLA.bit.SPEED = 1; //SPEED: Fm+ up to 1MHz (No sync) + SERCOM1->I2CM.CTRLA.bit.RUNSTDBY = 1; //RUNSTBY: Enabled (No sync) + + SERCOM1->I2CM.CTRLB.bit.SMEN = 1; //SMEN: Smart mode enabled (For DMA)(No sync) + + NVIC_EnableIRQ(SERCOM1_0_IRQn); + SERCOM1->I2CM.INTENSET.bit.ERROR = 1; + + SERCOM1->I2CM.CTRLA.bit.ENABLE = 1; //ENABLE: Enable the device (sync SYNCBUSY.ENABLE) + while (SERCOM1->I2CM.SYNCBUSY.bit.ENABLE) { DBGC(DC_I2C1_INIT_SYNC_ENABLING); } //Wait for SYNCBUSY.ENABLE to clear + + SERCOM1->I2CM.STATUS.bit.BUSSTATE = 1; //BUSSTATE: Force into IDLE state (sync SYNCBUSY.SYSOP) + while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP) { DBGC(DC_I2C1_INIT_SYNC_SYSOP); } + while (SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1) { DBGC(DC_I2C1_INIT_WAIT_IDLE); } //Wait while not idle + + DBGC(DC_I2C1_INIT_COMPLETE); +} + +uint8_t i2c1_start(uint8_t address) +{ + SERCOM1->I2CM.ADDR.bit.ADDR = address; + while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP) {} + while (SERCOM1->I2CM.INTFLAG.bit.MB == 0) {} + while (SERCOM1->I2CM.STATUS.bit.RXNACK) {} + + return 1; +} + +uint8_t i2c1_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout) +{ + if (!length) return 0; + + i2c1_start(address); + + while (length) + { + SERCOM1->I2CM.DATA.bit.DATA = *data; + while (SERCOM1->I2CM.INTFLAG.bit.MB == 0) {} + while (SERCOM1->I2CM.STATUS.bit.RXNACK) {} + + data++; + length--; + } + + i2c1_stop(); + + return 1; +} + +void i2c1_stop(void) +{ + if (SERCOM1->I2CM.STATUS.bit.CLKHOLD || SERCOM1->I2CM.INTFLAG.bit.MB == 1 || SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1) + { + SERCOM1->I2CM.CTRLB.bit.CMD = 3; + while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP); + while (SERCOM1->I2CM.STATUS.bit.CLKHOLD); + while (SERCOM1->I2CM.INTFLAG.bit.MB); + while (SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1); + } +} + +void i2c_led_send_CRWL(uint8_t drvid) +{ + uint8_t i2cdata[] = { ISSI3733_CMDRWL, ISSI3733_CMDRWL_WRITE_ENABLE_ONCE }; + i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0); +} + +void i2c_led_select_page(uint8_t drvid, uint8_t pageno) +{ + uint8_t i2cdata[] = { ISSI3733_CMDR, pageno }; + i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0); +} + +void i2c_led_send_GCR(uint8_t drvid) +{ + uint8_t i2cdata[] = { ISSI3733_GCCR, 0x00 }; + + if (gcr_actual > LED_GCR_MAX) gcr_actual = LED_GCR_MAX; + i2cdata[1] = gcr_actual; + + i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0); +} + +void i2c_led_send_onoff(uint8_t drvid) +{ +#if I2C_LED_USE_DMA != 1 + if (!i2c_led_q_running) + { +#endif + i2c_led_send_CRWL(drvid); + i2c_led_select_page(drvid, 0); +#if I2C_LED_USE_DMA != 1 + } +#endif + + *issidrv[drvid].onoff = 0; //Force start location offset to zero + i2c1_transmit(issidrv[drvid].addr, issidrv[drvid].onoff, ISSI3733_PG0_BYTES, 0); +} + +void i2c_led_send_mode_op_gcr(uint8_t drvid, uint8_t mode, uint8_t operation) +{ + uint8_t i2cdata[] = { ISSI3733_CR, mode | operation, gcr_actual}; + i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0); +} + +void i2c_led_send_pur_pdr(uint8_t drvid, uint8_t pur, uint8_t pdr) +{ + uint8_t i2cdata[] = { ISSI3733_SWYR_PUR, pur, pdr }; + + i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0); +} + +void i2c_led_send_pwm(uint8_t drvid) +{ +#if I2C_LED_USE_DMA != 1 + if (!i2c_led_q_running) + { +#endif + i2c_led_send_CRWL(drvid); + i2c_led_select_page(drvid, 0); +#if I2C_LED_USE_DMA != 1 + } +#endif + + *issidrv[drvid].pwm = 0; //Force start location offset to zero + i2c1_transmit(issidrv[drvid].addr, issidrv[drvid].pwm, ISSI3733_PG1_BYTES, 0); +} + +uint8_t I2C3733_Init_Control(void) +{ + DBGC(DC_I2C3733_INIT_CONTROL_BEGIN); + + srdata.bit.SDB_N = 1; + SPI_WriteSRData(); + + CLK_delay_ms(1); + + srdata.bit.IRST = 0; + SPI_WriteSRData(); + + CLK_delay_ms(1); + + DBGC(DC_I2C3733_INIT_CONTROL_COMPLETE); + + return 1; +} + +uint8_t I2C3733_Init_Drivers(void) +{ + DBGC(DC_I2C3733_INIT_DRIVERS_BEGIN); + + gcr_actual = ISSI3733_GCR_DEFAULT; + gcr_actual_last = gcr_actual; + + if (gcr_actual > LED_GCR_MAX) gcr_actual = LED_GCR_MAX; + gcr_desired = gcr_actual; + + //Set up master device + i2c_led_send_CRWL(0); + i2c_led_select_page(0, 3); + i2c_led_send_mode_op_gcr(0, ISSI3733_CR_SYNC_MASTER, ISSI3733_CR_SSD_NORMAL); + + //Set up slave device + i2c_led_send_CRWL(1); + i2c_led_select_page(1, 3); + i2c_led_send_mode_op_gcr(1, ISSI3733_CR_SYNC_SLAVE, ISSI3733_CR_SSD_NORMAL); + + i2c_led_send_CRWL(0); + i2c_led_select_page(0, 3); + i2c_led_send_pur_pdr(0, ISSI3733_SWYR_PUR_8000, ISSI3733_CSXR_PDR_8000); + + i2c_led_send_CRWL(1); + i2c_led_select_page(1, 3); + i2c_led_send_pur_pdr(1, ISSI3733_SWYR_PUR_8000, ISSI3733_CSXR_PDR_8000); + + DBGC(DC_I2C3733_INIT_DRIVERS_COMPLETE); + + return 1; +} + +void I2C_DMAC_LED_Init(void) +{ + Dmac *dmac = DMAC; + + DBGC(DC_I2C_DMAC_LED_INIT_BEGIN); + + //Disable device + dmac->CTRL.bit.DMAENABLE = 0; //Disable DMAC + while (dmac->CTRL.bit.DMAENABLE) {} //Wait for disabled state in case of ongoing transfers + dmac->CTRL.bit.SWRST = 1; //Software Reset DMAC + while (dmac->CTRL.bit.SWRST) {} //Wait for software reset to complete + + //Configure device + dmac->BASEADDR.reg = (uint32_t)&dmac_desc; //Set descriptor base address + dmac->WRBADDR.reg = (uint32_t)&dmac_desc_wb; //Set descriptor write back address + dmac->CTRL.reg |= 0x0f00; //Handle all priorities (LVL0-3) + + //Disable channel + dmac->Channel[0].CHCTRLA.bit.ENABLE = 0; //Disable the channel + while (dmac->Channel[0].CHCTRLA.bit.ENABLE) {} //Wait for disabled state in case of ongoing transfers + dmac->Channel[0].CHCTRLA.bit.SWRST = 1; //Software Reset the channel + while (dmac->Channel[0].CHCTRLA.bit.SWRST) {} //Wait for software reset to complete + + //Configure channel + dmac->Channel[0].CHCTRLA.bit.THRESHOLD = 0; //1BEAT + dmac->Channel[0].CHCTRLA.bit.BURSTLEN = 0; //SINGLE + dmac->Channel[0].CHCTRLA.bit.TRIGACT = 2; //BURST + dmac->Channel[0].CHCTRLA.bit.TRIGSRC = SERCOM1_DMAC_ID_TX; //Trigger source + dmac->Channel[0].CHCTRLA.bit.RUNSTDBY = 1; //Run in standby + + NVIC_EnableIRQ(DMAC_0_IRQn); + dmac->Channel[0].CHINTENSET.bit.TCMPL = 1; + dmac->Channel[0].CHINTENSET.bit.TERR = 1; + + //Enable device + dmac->CTRL.bit.DMAENABLE = 1; //Enable DMAC + while (dmac->CTRL.bit.DMAENABLE == 0) {} //Wait for enable state + + DBGC(DC_I2C_DMAC_LED_INIT_COMPLETE); +} + +//state = 1 enable +//state = 0 disable +void I2C3733_Control_Set(uint8_t state) +{ + DBGC(DC_I2C3733_CONTROL_SET_BEGIN); + + srdata.bit.SDB_N = (state == 1 ? 1 : 0); + SPI_WriteSRData(); + + DBGC(DC_I2C3733_CONTROL_SET_COMPLETE); +} + +void i2c_led_desc_defaults(void) +{ + dmac_desc.BTCTRL.bit.STEPSIZE = 0; //SRCINC used in favor for auto 1 inc + dmac_desc.BTCTRL.bit.STEPSEL = 0; //SRCINC used in favor for auto 1 inc + dmac_desc.BTCTRL.bit.DSTINC = 0; //The Destination Address Increment is disabled + dmac_desc.BTCTRL.bit.SRCINC = 1; //The Source Address Increment is enabled (Inc by 1) + dmac_desc.BTCTRL.bit.BEATSIZE = 0; //8-bit bus transfer + dmac_desc.BTCTRL.bit.BLOCKACT = 0; //Channel will be disabled if it is the last block transfer in the transaction + dmac_desc.BTCTRL.bit.EVOSEL = 0; //Event generation disabled + dmac_desc.BTCTRL.bit.VALID = 1; //Set dmac valid +} + +void i2c_led_prepare_send_dma(uint8_t *data, uint8_t len) +{ + i2c_led_desc_defaults(); + + dmac_desc.BTCNT.reg = len; + dmac_desc.SRCADDR.reg = (uint32_t)data + len; + dmac_desc.DSTADDR.reg = (uint32_t)&SERCOM1->I2CM.DATA.reg; + dmac_desc.DESCADDR.reg = 0; +} + +void i2c_led_begin_dma(uint8_t drvid) +{ + DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1; //Enable the channel + + SERCOM1->I2CM.ADDR.reg = (dmac_desc.BTCNT.reg << 16) | 0x2000 | issidrv[drvid].addr; //Begin transfer +} + +void i2c_led_send_CRWL_dma(uint8_t drvid) +{ + *(dma_sendbuf+0) = ISSI3733_CMDRWL; + *(dma_sendbuf+1) = ISSI3733_CMDRWL_WRITE_ENABLE_ONCE; + i2c_led_prepare_send_dma(dma_sendbuf, 2); + + i2c_led_begin_dma(drvid); +} + +void i2c_led_select_page_dma(uint8_t drvid, uint8_t pageno) +{ + *(dma_sendbuf+0) = ISSI3733_CMDR; + *(dma_sendbuf+1) = pageno; + i2c_led_prepare_send_dma(dma_sendbuf, 2); + + i2c_led_begin_dma(drvid); +} + +void i2c_led_send_GCR_dma(uint8_t drvid) +{ + *(dma_sendbuf+0) = ISSI3733_GCCR; + *(dma_sendbuf+1) = gcr_actual; + i2c_led_prepare_send_dma(dma_sendbuf, 2); + + i2c_led_begin_dma(drvid); +} + +void i2c_led_send_pwm_dma(uint8_t drvid) +{ + //Note: This copies the CURRENT pwm buffer, which may be getting modified + memcpy(dma_sendbuf, issidrv[drvid].pwm, ISSI3733_PG1_BYTES); + *dma_sendbuf = 0; //Force start location offset to zero + i2c_led_prepare_send_dma(dma_sendbuf, ISSI3733_PG1_BYTES); + + i2c_led_begin_dma(drvid); +} + +void i2c_led_send_onoff_dma(uint8_t drvid) +{ + //Note: This copies the CURRENT onoff buffer, which may be getting modified + memcpy(dma_sendbuf, issidrv[drvid].onoff, ISSI3733_PG0_BYTES); + *dma_sendbuf = 0; //Force start location offset to zero + i2c_led_prepare_send_dma(dma_sendbuf, ISSI3733_PG0_BYTES); + + i2c_led_begin_dma(drvid); +} + +void i2c_led_q_init(void) +{ + memset(i2c_led_q, 0, I2C_Q_SIZE); + i2c_led_q_s = 0; + i2c_led_q_e = 0; + i2c_led_q_running = 0; + i2c_led_q_full = 0; +} + +uint8_t i2c_led_q_isempty(void) +{ + return i2c_led_q_s == i2c_led_q_e; +} + +uint8_t i2c_led_q_size(void) +{ + return (i2c_led_q_e - i2c_led_q_s) % I2C_Q_SIZE; +} + +uint8_t i2c_led_q_available(void) +{ + return I2C_Q_SIZE - i2c_led_q_size() - 1; //Never allow end to meet start +} + +void i2c_led_q_add(uint8_t cmd) +{ + //WARNING: Always request room before adding commands! + + //Assign command + i2c_led_q[i2c_led_q_e] = cmd; + + i2c_led_q_e = (i2c_led_q_e + 1) % I2C_Q_SIZE; //Move end up one or wrap +} + +void i2c_led_q_s_advance(void) +{ + i2c_led_q_s = (i2c_led_q_s + 1) % I2C_Q_SIZE; //Move start up one or wrap +} + +//Always request room before adding commands +//PS: In case the queue somehow gets filled, it will reset if it can not clear up +//PS: Could only get this to happen through unrealistic timings to overload the I2C bus +uint8_t i2c_led_q_request_room(uint8_t request_size) +{ + if (request_size > i2c_led_q_available()) + { + i2c_led_q_full++; + + if (i2c_led_q_full >= 100) //Give the queue a chance to clear up + { + led_on; + I2C_DMAC_LED_Init(); + i2c_led_q_init(); + return 1; + } + + return 0; + } + + i2c_led_q_full = 0; + + return 1; +} + +uint8_t i2c_led_q_run(void) +{ + if (i2c_led_q_isempty()) + { + i2c_led_q_running = 0; + return 0; + } + + if (i2c_led_q_running) return 1; + + i2c_led_q_running = 1; + +#if I2C_LED_USE_DMA != 1 + while (!i2c_led_q_isempty()) + { +#endif + //run command + if (i2c_led_q[i2c_led_q_s] == I2C_Q_CRWL) + { + i2c_led_q_s_advance(); + uint8_t drvid = i2c_led_q[i2c_led_q_s]; +#if I2C_LED_USE_DMA == 1 + i2c_led_send_CRWL_dma(drvid); +#else + i2c_led_send_CRWL(drvid); +#endif + } + else if (i2c_led_q[i2c_led_q_s] == I2C_Q_PAGE_SELECT) + { + i2c_led_q_s_advance(); + uint8_t drvid = i2c_led_q[i2c_led_q_s]; + i2c_led_q_s_advance(); + uint8_t page = i2c_led_q[i2c_led_q_s]; +#if I2C_LED_USE_DMA == 1 + i2c_led_select_page_dma(drvid, page); +#else + i2c_led_select_page(drvid, page); +#endif + } + else if (i2c_led_q[i2c_led_q_s] == I2C_Q_PWM) + { + i2c_led_q_s_advance(); + uint8_t drvid = i2c_led_q[i2c_led_q_s]; +#if I2C_LED_USE_DMA == 1 + i2c_led_send_pwm_dma(drvid); +#else + i2c_led_send_pwm(drvid); +#endif + } + else if (i2c_led_q[i2c_led_q_s] == I2C_Q_GCR) + { + i2c_led_q_s_advance(); + uint8_t drvid = i2c_led_q[i2c_led_q_s]; +#if I2C_LED_USE_DMA == 1 + i2c_led_send_GCR_dma(drvid); +#else + i2c_led_send_GCR(drvid); +#endif + } + else if (i2c_led_q[i2c_led_q_s] == I2C_Q_ONOFF) + { + i2c_led_q_s_advance(); + uint8_t drvid = i2c_led_q[i2c_led_q_s]; +#if I2C_LED_USE_DMA == 1 + i2c_led_send_onoff_dma(drvid); +#else + i2c_led_send_onoff(drvid); +#endif + } + + i2c_led_q_s_advance(); //Advance last run command or if the command byte was not serviced + +#if I2C_LED_USE_DMA != 1 + } + + i2c_led_q_running = 0; +#endif + + return 1; +} +#endif //MD_BOOTLOADER diff --git a/tmk_core/protocol/arm_atsam/i2c_master.h b/tmk_core/protocol/arm_atsam/i2c_master.h new file mode 100644 index 0000000000..99481366a5 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/i2c_master.h @@ -0,0 +1,108 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef _I2C_MASTER_H_ +#define _I2C_MASTER_H_ + +#ifndef MD_BOOTLOADER + +#include "samd51j18a.h" +#include "issi3733_driver.h" +#include "config.h" + +__attribute__((__aligned__(16))) +DmacDescriptor dmac_desc; +__attribute__((__aligned__(16))) +DmacDescriptor dmac_desc_wb; + +uint8_t I2C3733_Init_Control(void); +uint8_t I2C3733_Init_Drivers(void); +void I2C3733_Control_Set(uint8_t state); +void I2C_DMAC_LED_Init(void); + +#define I2C_Q_SIZE 100 + +#define I2C_Q_NA 100 +#define I2C_Q_CRWL 101 +#define I2C_Q_PAGE_SELECT 102 +#define I2C_Q_PWM 103 +#define I2C_Q_GCR 104 +#defi