diff --git a/fusee/fusee-primary/src/car.h b/fusee/fusee-primary/src/car.h index b6b8575ec..0fc185f4e 100644 --- a/fusee/fusee-primary/src/car.h +++ b/fusee/fusee-primary/src/car.h @@ -55,6 +55,16 @@ enum { CLK_DIVIDER_32 = 32, }; + +/** + * Reset bits for relevant registers. + */ +enum { + CAR_CONTROL_SDMMC1 = (1 << 14), + CAR_CONTROL_SDMMC4 = (1 << 15), +}; + + enum { CLK_SOURCE_SDMMC1 = 19, CLK_SOURCE_SDMMC4 = 21, /* 0x54 into the the main source block */ diff --git a/fusee/fusee-primary/src/gpio.c b/fusee/fusee-primary/src/gpio.c new file mode 100644 index 000000000..fd44a0f67 --- /dev/null +++ b/fusee/fusee-primary/src/gpio.c @@ -0,0 +1,149 @@ +#include +#include +#include + +#include "gpio.h" +#include "lib/printk.h" + +enum tegra_gpio_shifts { + GPIO_BANK_SHIFT = 5, + GPIO_PORT_SHIFT = 3, +}; + +enum tegra_gpio_masks { + GPIO_PORT_MASK = 0x3, + GPIO_PIN_MASK = 0x7, +}; + +/** + * Returns a GPIO bank object that corresponds to the given GPIO pin, + * which can be created using the TEGRA_GPIO macro or passed from the name macro. + * + * @param pin The GPIO to get the bank for. + * @return The GPIO bank object to use for working with the given bank. + */ +static volatile struct tegra_gpio_bank *gpio_get_bank(enum tegra_named_gpio pin) +{ + volatile struct tegra_gpio *gpio = gpio_get_regs(); + int bank_number = pin >> GPIO_BANK_SHIFT; + + return &gpio->bank[bank_number]; +} + + +/** + * @return the port number for working with the given GPIO. + */ +static volatile int gpio_get_port(enum tegra_named_gpio pin) +{ + return (pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK; +} + + +/** + * @return a mask to be used to work with the given GPIO + */ +static volatile uint32_t gpio_get_mask(enum tegra_named_gpio pin) +{ + uint32_t pin_number = pin & GPIO_PIN_MASK; + return (1 << pin_number); +} + + + +/** + * Performs a simple GPIO configuration operation. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param should_be_set True iff the relevant bit should be set; or false if it should be cleared. + * @param offset The offset into a gpio_bank structure + */ +static void gpio_simple_register_set(enum tegra_named_gpio pin, bool should_be_set, size_t offset) +{ + // Retrieve the register set that corresponds to the given pin and offset. + uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset; + uint32_t *cluster = (uint32_t *)cluster_addr; + + // Figure out the offset into the cluster, + // and the mask to be used. + int port = gpio_get_port(pin); + uint32_t mask = gpio_get_mask(pin); + + + // Set or clear the bit, as appropriate. + if (should_be_set) + cluster[port] |= mask; + else + cluster[port] &= ~mask; +} + + +/** + * Performs a simple GPIO configuration operation. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param should_be_set True iff the relevant bit should be set; or false if it should be cleared. + * @param offset The offset into a gpio_bank structure + */ +static bool gpio_simple_register_get(enum tegra_named_gpio pin, size_t offset) +{ + // Retrieve the register set that corresponds to the given pin and offset. + uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset; + uint32_t *cluster = (uint32_t *)cluster_addr; + + // Figure out the offset into the cluster, + // and the mask to be used. + int port = gpio_get_port(pin); + uint32_t mask = gpio_get_mask(pin); + + // Convert the given value to a boolean. + return !!(cluster[port] & mask); +} + + +/** + * Configures a given pin as either GPIO or SFIO. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param mode The relevant mode. + */ +void gpio_configure_mode(enum tegra_named_gpio pin, enum tegra_gpio_mode mode) +{ + gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(struct tegra_gpio_bank, config)); +} + + +/** + * Configures a given pin as either INPUT or OUPUT. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param direction The relevant direction. + */ +void gpio_configure_direction(enum tegra_named_gpio pin, enum tegra_gpio_direction dir) +{ + gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(struct tegra_gpio_bank, direction)); +} + + +/** + * Drives a relevant GPIO pin as either HIGH or LOW. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param mode The relevant mode. + */ +void gpio_write(enum tegra_named_gpio pin, enum tegra_gpio_value value) +{ + gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(struct tegra_gpio_bank, out)); +} + + +/** + * Drives a relevant GPIO pin as either HIGH or LOW. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param mode The relevant mode. + */ +enum tegra_gpio_value gpio_read(enum tegra_named_gpio pin) +{ + return gpio_simple_register_get(pin, offsetof(struct tegra_gpio_bank, in)); +} diff --git a/fusee/fusee-primary/src/gpio.h b/fusee/fusee-primary/src/gpio.h new file mode 100644 index 000000000..581906c8b --- /dev/null +++ b/fusee/fusee-primary/src/gpio.h @@ -0,0 +1,174 @@ +/* + * Struct defintiions lifted from NVIDIA sample code. + * (C) Copyright 2013-2015 NVIDIA Corporation + * + * adapted for Fusée by Kate Temkin +#include +#include "utils.h" + +enum tegra_gpio_port { + TEGRA_GPIO_PORT_A = 0, + TEGRA_GPIO_PORT_B = 1, + TEGRA_GPIO_PORT_C = 2, + TEGRA_GPIO_PORT_D = 3, + TEGRA_GPIO_PORT_E = 4, + TEGRA_GPIO_PORT_F = 5, + TEGRA_GPIO_PORT_G = 6, + TEGRA_GPIO_PORT_H = 7, + TEGRA_GPIO_PORT_I = 8, + TEGRA_GPIO_PORT_J = 9, + TEGRA_GPIO_PORT_K = 10, + TEGRA_GPIO_PORT_L = 11, + TEGRA_GPIO_PORT_M = 12, + TEGRA_GPIO_PORT_N = 13, + TEGRA_GPIO_PORT_O = 14, + TEGRA_GPIO_PORT_P = 15, + TEGRA_GPIO_PORT_Q = 16, + TEGRA_GPIO_PORT_R = 17, + TEGRA_GPIO_PORT_S = 18, + TEGRA_GPIO_PORT_T = 19, + TEGRA_GPIO_PORT_U = 20, + TEGRA_GPIO_PORT_V = 21, + TEGRA_GPIO_PORT_W = 22, + TEGRA_GPIO_PORT_X = 23, + TEGRA_GPIO_PORT_Y = 24, + TEGRA_GPIO_PORT_Z = 25, + TEGRA_GPIO_PORT_AA = 26, + TEGRA_GPIO_PORT_BB = 27, + TEGRA_GPIO_PORT_CC = 28, + TEGRA_GPIO_PORT_DD = 29, + TEGRA_GPIO_PORT_EE = 30, + TEGRA_GPIO_PORT_FF = 31, +}; + +/** + * Convenince macro for computing a GPIO port number. + */ +#define TEGRA_GPIO(port, offset) \ + ((TEGRA_GPIO_PORT_##port * 8) + offset) + +/* + * The Tegra210 GPIO controller has 256 GPIOS in 8 banks of 4 ports, + * each with 8 GPIOs. + */ +enum { + TEGRA_GPIO_PORTS = 4, /* number of ports per bank */ + TEGRA_GPIO_BANKS = 8, /* number of banks */ +}; + +/* GPIO Controller registers for a single bank */ +struct tegra_gpio_bank { + uint32_t config[TEGRA_GPIO_PORTS]; + uint32_t direction[TEGRA_GPIO_PORTS]; + uint32_t out[TEGRA_GPIO_PORTS]; + uint32_t in[TEGRA_GPIO_PORTS]; + uint32_t int_status[TEGRA_GPIO_PORTS]; + uint32_t int_enable[TEGRA_GPIO_PORTS]; + uint32_t int_level[TEGRA_GPIO_PORTS]; + uint32_t int_clear[TEGRA_GPIO_PORTS]; + uint32_t masked_config[TEGRA_GPIO_PORTS]; + uint32_t masked_dir_out[TEGRA_GPIO_PORTS]; + uint32_t masked_out[TEGRA_GPIO_PORTS]; + uint32_t masked_in[TEGRA_GPIO_PORTS]; + uint32_t masked_int_status[TEGRA_GPIO_PORTS]; + uint32_t masked_int_enable[TEGRA_GPIO_PORTS]; + uint32_t masked_int_level[TEGRA_GPIO_PORTS]; + uint32_t masked_int_clear[TEGRA_GPIO_PORTS]; +}; + + +/** + * Representation of Tegra GPIO controllers. + */ +struct tegra_gpio { + struct tegra_gpio_bank bank[TEGRA_GPIO_BANKS]; +}; + +/** + * GPIO pins that have a more detailed functional name, + * specialized for the Switch. + */ +enum tegra_named_gpio { + GPIO_MICROSD_CARD_DETECT = TEGRA_GPIO(Z, 1), + GPIO_MICROSD_WRITE_PROTECT = TEGRA_GPIO(Z, 4), + GPIO_MICROSD_SUPPLY_ENABLE = TEGRA_GPIO(E, 4), +}; + + +/** + * Mode select for GPIO or SFIO. + */ +enum tegra_gpio_mode { + GPIO_MODE_GPIO = 0, + GPIO_MODE_SFIO = 1 +}; + + +/** + * GPIO direction values + */ +enum tegra_gpio_direction { + GPIO_DIRECTION_INPUT = 0, + GPIO_DIRECTION_OUTPUT = 1 +}; + + +/** + * Active-high GPIO logic + */ +enum tegra_gpio_value { + GPIO_LEVEL_LOW = 0, + GPIO_LEVEL_HIGH = 1 +}; + + +/** + * Utility function that grabs the Tegra pinmux registers. + */ +static inline struct tegra_gpio *gpio_get_regs(void) +{ + return (struct tegra_gpio *)0x6000d000; +} + +/** + * Configures a given pin as either GPIO or SFIO. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param mode The relevant mode. + */ +void gpio_configure_mode(enum tegra_named_gpio pin, enum tegra_gpio_mode mode); + + +/** + * Configures a given pin as either INPUT or OUPUT. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param direction The relevant direction. + */ +void gpio_configure_direction(enum tegra_named_gpio pin, enum tegra_gpio_direction dir); + + +/** + * Drives a relevant GPIO pin as either HIGH or LOW. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param mode The relevant value. + */ +void gpio_write(enum tegra_named_gpio pin, enum tegra_gpio_value value); + +/** + * Drives a relevant GPIO pin as either HIGH or LOW. + * + * @param pin The GPIO pin to work with, as created with TEGRA_GPIO, or a named GPIO. + * @param mode The relevant mode. + */ +enum tegra_gpio_value gpio_read(enum tegra_named_gpio pin); + +#endif diff --git a/fusee/fusee-primary/src/hwinit/max77620.h b/fusee/fusee-primary/src/hwinit/max77620.h index d5e4eff3b..7223067dc 100644 --- a/fusee/fusee-primary/src/hwinit/max77620.h +++ b/fusee/fusee-primary/src/hwinit/max77620.h @@ -1,15 +1,15 @@ /* -* Defining registers address and its bit definitions of MAX77620 and MAX20024 -* -* Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. -* -* This program is free software; you can redistribute it and/or modify it -* under the terms and conditions of the GNU General Public License, -* version 2, as published by the Free Software Foundation. -*/ + * Defining registers address and its bit definitions of MAX77620 and MAX20024 + * + * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ -#ifndef _MAX77620_H_ -#define _MAX77620_H_ +#ifndef _MFD_MAX77620_H_ +#define _MFD_MAX77620_H_ /* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */ #define MAX77620_REG_CNFGGLBL1 0x00 @@ -72,7 +72,7 @@ #define MAX77620_LDO_SLEW_RATE_MASK 0x1 /* LDO Configuration 3 */ -#define MAX77620_TRACK4_MASK BIT(5) +#define MAX77620_TRACK4_MASK (1 << 5) #define MAX77620_TRACK4_SHIFT 5 /* Voltage */ @@ -113,6 +113,29 @@ #define MAX77620_REG_FPS_SD2 0x51 #define MAX77620_REG_FPS_SD3 0x52 #define MAX77620_REG_FPS_SD4 0x53 +#define MAX77620_REG_FPS_NONE 0 + +#define MAX77620_FPS_SRC_MASK 0xC0 +#define MAX77620_FPS_SRC_SHIFT 6 +#define MAX77620_FPS_PU_PERIOD_MASK 0x38 +#define MAX77620_FPS_PU_PERIOD_SHIFT 3 +#define MAX77620_FPS_PD_PERIOD_MASK 0x07 +#define MAX77620_FPS_PD_PERIOD_SHIFT 0 +#define MAX77620_FPS_TIME_PERIOD_MASK 0x38 +#define MAX77620_FPS_TIME_PERIOD_SHIFT 3 +#define MAX77620_FPS_EN_SRC_MASK 0x06 +#define MAX77620_FPS_EN_SRC_SHIFT 1 +#define MAX77620_FPS_ENFPS_SW_MASK 0x01 +#define MAX77620_FPS_ENFPS_SW 0x01 + +/* Minimum and maximum FPS period time (in microseconds) are + * different for MAX77620 and Max20024. + */ +#define MAX77620_FPS_PERIOD_MIN_US 40 +#define MAX20024_FPS_PERIOD_MIN_US 20 + +#define MAX77620_FPS_PERIOD_MAX_US 2560 +#define MAX20024_FPS_PERIOD_MAX_US 5120 #define MAX77620_REG_FPS_GPIO1 0x54 #define MAX77620_REG_FPS_GPIO2 0x55 @@ -128,4 +151,174 @@ #define MAX77620_REG_DVSSD4 0x5E #define MAX20024_REG_MAX_ADD 0x70 -#endif +#define MAX77620_CID_DIDM_MASK 0xF0 +#define MAX77620_CID_DIDM_SHIFT 4 + +/* CNCG2SD */ +#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1) +#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2) + +/* Device Identification Metal */ +#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF) +/* Device Indentification OTP */ +#define MAX77620_CID5_DIDO(n) ((n) & 0xF) + +/* SD CNFG1 */ +#define MAX77620_SD_SR_MASK 0xC0 +#define MAX77620_SD_SR_SHIFT 6 +#define MAX77620_SD_POWER_MODE_MASK 0x30 +#define MAX77620_SD_POWER_MODE_SHIFT 4 +#define MAX77620_SD_CFG1_ADE_MASK (1 << 3) +#define MAX77620_SD_CFG1_ADE_DISABLE 0 +#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3) +#define MAX77620_SD_FPWM_MASK 0x04 +#define MAX77620_SD_FPWM_SHIFT 2 +#define MAX77620_SD_FSRADE_MASK 0x01 +#define MAX77620_SD_FSRADE_SHIFT 0 +#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2) +#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 +#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2) +#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1) +#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0) +#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 +#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) + +/* LDO_CNFG2 */ +#define MAX77620_LDO_POWER_MODE_MASK 0xC0 +#define MAX77620_LDO_POWER_MODE_SHIFT 6 +#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2) +#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1) +#define MAX77620_LDO_CFG2_ADE_DISABLE 0 +#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1) +#define MAX77620_LDO_CFG2_SS_MASK (1 << 0) +#define MAX77620_LDO_CFG2_SS_FAST (1 << 0) +#define MAX77620_LDO_CFG2_SS_SLOW 0 + +#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7) +#define MAX77620_IRQ_TOP_SD_MASK (1 << 6) +#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5) +#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4) +#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3) +#define MAX77620_IRQ_TOP_32K_MASK (1 << 2) +#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1) + +#define MAX77620_IRQ_LBM_MASK (1 << 3) +#define MAX77620_IRQ_TJALRM1_MASK (1 << 2) +#define MAX77620_IRQ_TJALRM2_MASK (1 << 1) + +#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN 0 +#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_OUTPUT 0 +#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW 0 +#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4) +#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4) +#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5) +#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6) +#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6) +#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6) +#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6) +#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6) + +#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0) +#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1) +#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2) +#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3) +#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4) +#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5) +#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6) +#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7) + +#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2) + +#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7) +#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38 +#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3 +#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2) +#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1) +#define MAX20024_ONOFFCNFG1_CLRSE 0x18 + +#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7) +#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6) +#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5) +#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2) +#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0) + +#define MAX77620_GLBLM_MASK (1 << 0) + +#define MAX77620_WDTC_MASK 0x3 +#define MAX77620_WDTOFFC (1 << 4) +#define MAX77620_WDTSLPC (1 << 3) +#define MAX77620_WDTEN (1 << 2) + +#define MAX77620_TWD_MASK 0x3 +#define MAX77620_TWD_2s 0x0 +#define MAX77620_TWD_16s 0x1 +#define MAX77620_TWD_64s 0x2 +#define MAX77620_TWD_128s 0x3 + +#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7) +#define MAX77620_CNFGGLBL1_MPPLD (1 << 6) +#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4)) +#define MAX77620_CNFGGLBL1_LBDAC 0x0E +#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0) + +/* CNFG BBC registers */ +#define MAX77620_CNFGBBC_ENABLE (1 << 0) +#define MAX77620_CNFGBBC_CURRENT_MASK 0x06 +#define MAX77620_CNFGBBC_CURRENT_SHIFT 1 +#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18 +#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3 +#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5) +#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 +#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 + +#define MAX77620_FPS_COUNT 3 + +/* Interrupts */ +enum { + MAX77620_IRQ_TOP_GLBL, /* Low-Battery */ + MAX77620_IRQ_TOP_SD, /* SD power fail */ + MAX77620_IRQ_TOP_LDO, /* LDO power fail */ + MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */ + MAX77620_IRQ_TOP_RTC, /* RTC */ + MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */ + MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */ + MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */ +}; + +/* GPIOs */ +enum { + MAX77620_GPIO0, + MAX77620_GPIO1, + MAX77620_GPIO2, + MAX77620_GPIO3, + MAX77620_GPIO4, + MAX77620_GPIO5, + MAX77620_GPIO6, + MAX77620_GPIO7, + MAX77620_GPIO_NR, +}; + +/* FPS Source */ +enum max77620_fps_src { + MAX77620_FPS_SRC_0, + MAX77620_FPS_SRC_1, + MAX77620_FPS_SRC_2, + MAX77620_FPS_SRC_NONE, + MAX77620_FPS_SRC_DEF, +}; + +enum max77620_chip_id { + MAX77620, + MAX20024, +}; + +#endif /* _MFD_MAX77620_H_ */ diff --git a/fusee/fusee-primary/src/hwinit/max7762x.c b/fusee/fusee-primary/src/hwinit/max7762x.c new file mode 100755 index 000000000..67caaf230 --- /dev/null +++ b/fusee/fusee-primary/src/hwinit/max7762x.c @@ -0,0 +1,136 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 "max7762x.h" +#include "max77620.h" +#include "i2c.h" +#include "util.h" + +#define REGULATOR_SD 0 +#define REGULATOR_LDO 1 + +typedef struct _max77620_regulator_t +{ + u8 type; + const char *name; + u8 reg_sd; + u32 mv_step; + u32 mv_min; + u32 mv_default; + u32 mv_max; + u8 volt_addr; + u8 cfg_addr; + u8 volt_mask; + u8 enable_mask; + u8 enable_shift; + u8 status_mask; + + u8 fps_addr; + u8 fps_src; + u8 pd_period; + u8 pu_period; +} max77620_regulator_t; + +static const max77620_regulator_t _pmic_regulators[] = { + { REGULATOR_SD, "sd0", 0x16, 12500, 600000, 625000, 1400000, MAX77620_REG_SD0, MAX77620_REG_SD0_CFG, 0x3F, 0x30, 4, 0x80, 0x4F, 1, 7, 1 }, + { REGULATOR_SD, "sd1", 0x17, 12500, 600000, 1125000, 1125000, MAX77620_REG_SD1, MAX77620_REG_SD1_CFG, 0x3F, 0x30, 4, 0x40, 0x50, 0, 1, 5 }, + { REGULATOR_SD, "sd2", 0x18, 12500, 600000, 1325000, 1350000, MAX77620_REG_SD2, MAX77620_REG_SD2_CFG, 0xFF, 0x30, 4, 0x20, 0x51, 1, 5, 2 }, + { REGULATOR_SD, "sd3", 0x19, 12500, 600000, 1800000, 1800000, MAX77620_REG_SD3, MAX77620_REG_SD3_CFG, 0xFF, 0x30, 4, 0x10, 0x52, 0, 3, 3 }, + { REGULATOR_LDO, "ldo0", 0x00, 25000, 800000, 1200000, 1200000, MAX77620_REG_LDO0_CFG, MAX77620_REG_LDO0_CFG2, 0x3F, 0xC0, 6, 0x00, 0x46, 3, 7, 0 }, + { REGULATOR_LDO, "ldo1", 0x00, 25000, 800000, 1050000, 1050000, MAX77620_REG_LDO1_CFG, MAX77620_REG_LDO1_CFG2, 0x3F, 0xC0, 6, 0x00, 0x47, 3, 7, 0 }, + { REGULATOR_LDO, "ldo2", 0x00, 50000, 800000, 1800000, 3300000, MAX77620_REG_LDO2_CFG, MAX77620_REG_LDO2_CFG2, 0x3F, 0xC0, 6, 0x00, 0x48, 3, 7, 0 }, + { REGULATOR_LDO, "ldo3", 0x00, 50000, 800000, 3100000, 3100000, MAX77620_REG_LDO3_CFG, MAX77620_REG_LDO3_CFG2, 0x3F, 0xC0, 6, 0x00, 0x49, 3, 7, 0 }, + { REGULATOR_LDO, "ldo4", 0x00, 12500, 800000, 850000, 850000, MAX77620_REG_LDO4_CFG, MAX77620_REG_LDO4_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4A, 0, 7, 1 }, + { REGULATOR_LDO, "ldo5", 0x00, 50000, 800000, 1800000, 1800000, MAX77620_REG_LDO5_CFG, MAX77620_REG_LDO5_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4B, 3, 7, 0 }, + { REGULATOR_LDO, "ldo6", 0x00, 50000, 800000, 2900000, 2900000, MAX77620_REG_LDO6_CFG, MAX77620_REG_LDO6_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4C, 3, 7, 0 }, + { REGULATOR_LDO, "ldo7", 0x00, 50000, 800000, 1050000, 1050000, MAX77620_REG_LDO7_CFG, MAX77620_REG_LDO7_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4D, 1, 4, 3 }, + { REGULATOR_LDO, "ldo8", 0x00, 50000, 800000, 1050000, 1050000, MAX77620_REG_LDO8_CFG, MAX77620_REG_LDO8_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4E, 3, 7, 0 } +}; + +int max77620_regulator_get_status(u32 id) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (reg->type == REGULATOR_SD) + return i2c_recv_byte(I2C_5, 0x3C, MAX77620_REG_STATSD) & reg->status_mask ? 0 : 1; + return i2c_recv_byte(I2C_5, 0x3C, reg->cfg_addr) & 8 ? 1 : 0; +} + +int max77620_regulator_config_fps(u32 id) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + i2c_send_byte(I2C_5, 0x3C, reg->fps_addr, (reg->fps_src << 6) | (reg->pu_period << 3) | (reg->pd_period)); + + return 1; +} + +int max77620_regulator_set_voltage(u32 id, u32 mv) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (mv < reg->mv_default || mv > reg->mv_max) + return 0; + + u32 mult = (mv + reg->mv_step - 1 - reg->mv_min) / reg->mv_step; + u8 val = i2c_recv_byte(I2C_5, 0x3C, reg->volt_addr); + val = (val & ~reg->volt_mask) | (mult & reg->volt_mask); + i2c_send_byte(I2C_5, 0x3C, reg->volt_addr, val); + sleep(1000); + + return 1; +} + +int max77620_regulator_enable(u32 id, int enable) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + u32 addr = reg->type == REGULATOR_SD ? reg->cfg_addr : reg->volt_addr; + u8 val = i2c_recv_byte(I2C_5, 0x3C, addr); + if (enable) + val = (val & ~reg->enable_mask) | ((3 << reg->enable_shift) & reg->enable_mask); + else + val &= ~reg->enable_mask; + i2c_send_byte(I2C_5, 0x3C, addr, val); + sleep(1000); + + return 1; +} + +void max77620_config_default() +{ + for (u32 i = 1; i <= REGULATOR_MAX; i++) + { + i2c_recv_byte(I2C_5, 0x3C, MAX77620_REG_CID4); + max77620_regulator_config_fps(i); + max77620_regulator_set_voltage(i, _pmic_regulators[i].mv_default); + if (_pmic_regulators[i].fps_src != MAX77620_FPS_SRC_NONE) + max77620_regulator_enable(i, 1); + } + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_SD_CFG2, 4); +} diff --git a/fusee/fusee-primary/src/hwinit/max7762x.h b/fusee/fusee-primary/src/hwinit/max7762x.h new file mode 100755 index 000000000..05434956e --- /dev/null +++ b/fusee/fusee-primary/src/hwinit/max7762x.h @@ -0,0 +1,68 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 _MAX7762X_H_ +#define _MAX7762X_H_ + +#include "types.h" + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | | 50000 | 800000 | 3100000 | 3100000 | +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | +* ldo5 | | 50000 | 800000 | 1800000 | 1800000 | +* ldo6 | | 50000 | 800000 | 2900000 | 2900000 | +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | +* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 | +*/ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +/*! MAX77620 partitions. */ +#define REGULATOR_SD0 0 +#define REGULATOR_SD1 1 +#define REGULATOR_SD2 2 +#define REGULATOR_SD3 3 +#define REGULATOR_LDO0 4 +#define REGULATOR_LDO1 5 +#define REGULATOR_LDO2 6 +#define REGULATOR_LDO3 7 +#define REGULATOR_LDO4 8 +#define REGULATOR_LDO5 9 +#define REGULATOR_LDO6 10 +#define REGULATOR_LDO7 11 +#define REGULATOR_LDO8 12 +#define REGULATOR_MAX 12 + +int max77620_regulator_get_status(u32 id); +int max77620_regulator_config_fps(u32 id); +int max77620_regulator_set_voltage(u32 id, u32 mv); +int max77620_regulator_enable(u32 id, int enable); +void max77620_config_default(); + +#endif diff --git a/fusee/fusee-primary/src/pad_control.h b/fusee/fusee-primary/src/pad_control.h new file mode 100644 index 000000000..be0215bcf --- /dev/null +++ b/fusee/fusee-primary/src/pad_control.h @@ -0,0 +1,67 @@ +/** + * Fusée pad control code + * ~ktemkin + */ + +#ifndef __FUSEE_PADCTL_H__ +#define __FUSEE_PADCTL_H__ + +#include "utils.h" + +/** + * Registers in the Misc Pad control region + */ +struct PACKED tegra_padctl { + /* TODO: support registers before? */ + uint32_t sdmmc1_control; + uint32_t sdmmc3_control; + uint32_t sdmmc2_control; + uint32_t sdmmc4_control; + + /* TODO: support registers after? */ + uint8_t _todo[656]; + + uint32_t vgpio_gpio_mux_sel; +}; + +/** + * Masks for Pad Control registers + */ +enum tegra_padctl_masks { + + /* SDMMC1 */ + PADCTL_SDMMC1_DEEP_LOOPBACK = (1 << 0), + + /* SDMMC3 */ + PADCTL_SDMMC3_DEEP_LOOPBACK = (1 << 0), + + /* SDMMC2 */ + PADCTL_SDMMC2_ENABLE_DATA_IN = (0xFF << 8), + PADCTL_SDMMC2_ENABLE_CLK_IN = (0x3 << 4), + PADCTL_SDMMC2_DEEP_LOOPBACK = (1 << 0), + + /* SDMMC4 */ + PADCTL_SDMMC4_ENABLE_DATA_IN = (0xFF << 8), + PADCTL_SDMMC4_ENABLE_CLK_IN = (0x3 << 4), + PADCTL_SDMMC4_DEEP_LOOPBACK = (1 << 0), + + /* VGPIO/GPIO */ + PADCTL_SDMMC1_CD_SOURCE = (1 << 0), + PADCTL_SDMMC1_WP_SOURCE = (1 << 1), + PADCTL_SDMMC3_CD_SOURCE = (1 << 2), + PADCTL_SDMMC3_WP_SOURCE = (1 << 3), + + +}; + + +/** + * Utility function that grabs the Tegra PADCTL registers. + */ +static inline struct tegra_padctl *padctl_get_regs(void) +{ + return (struct tegra_padctl *)0x700008d4; +} + + +#endif diff --git a/fusee/fusee-primary/src/pinmux.h b/fusee/fusee-primary/src/pinmux.h new file mode 100644 index 000000000..bdd3574e2 --- /dev/null +++ b/fusee/fusee-primary/src/pinmux.h @@ -0,0 +1,212 @@ +#ifndef __FUSEE_PINMUX_H__ +#define __FUSEE_PINMUX_H__ + +#include +#include +#include "utils.h" + +/** + * Pinmux structures. + */ +struct PACKED tegra_pinmux { + uint32_t sdmmc1_clk; + uint32_t sdmmc1_cmd; + uint32_t sdmmc1_dat3; + uint32_t sdmmc1_dat2; + uint32_t sdmmc1_dat1; + uint32_t sdmmc1_dat0; + uint32_t _r18; + uint32_t sdmmc3_clk; + uint32_t sdmmc3_cmd; + uint32_t sdmmc3_dat0; + uint32_t sdmmc3_dat1; + uint32_t sdmmc3_dat2; + uint32_t sdmmc3_dat3; + uint32_t _r34; + uint32_t pex_l0_rst_n; + uint32_t pex_l0_clkreq_n; + uint32_t pex_wake_n; + uint32_t pex_l1_rst_n; + uint32_t pex_l1_clkreq_n; + uint32_t sata_led_active; + uint32_t spi1_mosi; + uint32_t spi1_miso; + uint32_t spi1_sck; + uint32_t spi1_cs0; + uint32_t spi1_cs1; + uint32_t spi2_mosi; + uint32_t spi2_miso; + uint32_t spi2_sck; + uint32_t spi2_cs0; + uint32_t spi2_cs1; + uint32_t spi4_mosi; + uint32_t spi4_miso; + uint32_t spi4_sck; + uint32_t spi4_cs0; + uint32_t qspi_sck; + uint32_t qspi_cs_n; + uint32_t qspi_io0; + uint32_t qspi_io1; + uint32_t qspi_io2; + uint32_t qspi_io3; + uint32_t _ra0; + uint32_t dmic1_clk; + uint32_t dmic1_dat; + uint32_t dmic2_clk; + uint32_t dmic2_dat; + uint32_t dmic3_clk; + uint32_t dmic3_dat; + uint32_t gen1_i2c_scl; + uint32_t gen1_i2c_sda; + uint32_t gen2_i2c_scl; + uint32_t gen2_i2c_sda; + uint32_t gen3_i2c_scl; + uint32_t gen3_i2c_sda; + uint32_t cam_i2c_scl; + uint32_t cam_i2c_sda; + uint32_t pwr_i2c_scl; + uint32_t pwr_i2c_sda; + uint32_t uart1_tx; + uint32_t uart1_rx; + uint32_t uart1_rts; + uint32_t uart1_cts; + uint32_t uart2_tx; + uint32_t uart2_rx; + uint32_t uart2_rts; + uint32_t uart2_cts; + uint32_t uart3_tx; + uint32_t uart3_rx; + uint32_t uart3_rts; + uint32_t uart3_cts; + uint32_t uart4_tx; + uint32_t uart4_rx; + uint32_t uart4_rts; + uint32_t uart4_cts; + uint32_t dap1_fs; + uint32_t dap1_din; + uint32_t dap1_dout; + uint32_t dap1_sclk; + uint32_t dap2_fs; + uint32_t dap2_din; + uint32_t dap2_dout; + uint32_t dap2_sclk; + uint32_t dap4_fs; + uint32_t dap4_din; + uint32_t dap4_dout; + uint32_t dap4_sclk; + uint32_t cam1_mclk; + uint32_t cam2_mclk; + uint32_t jtag_rtck; + uint32_t clk_32k_in; + uint32_t clk_32k_out; + uint32_t batt_bcl; + uint32_t clk_req; + uint32_t cpu_pwr_req; + uint32_t pwr_int_n; + uint32_t shutdown; + uint32_t core_pwr_req; + uint32_t aud_mclk; + uint32_t dvfs_pwm; + uint32_t dvfs_clk; + uint32_t gpio_x1_aud; + uint32_t gpio_x3_aud; + uint32_t pcc7; + uint32_t hdmi_cec; + uint32_t hdmi_int_dp_hpd; + uint32_t spdif_out; + uint32_t spdif_in; + uint32_t usb_vbus_en0; + uint32_t usb_vbus_en1; + uint32_t dp_hpd0; + uint32_t wifi_en; + uint32_t wifi_rst; + uint32_t wifi_wake_ap; + uint32_t ap_wake_bt; + uint32_t bt_rst; + uint32_t bt_wake_ap; + uint32_t ap_wake_nfc; + uint32_t nfc_en; + uint32_t nfc_int; + uint32_t gps_en; + uint32_t gps_rst; + uint32_t cam_rst; + uint32_t cam_af_en; + uint32_t cam_flash_en; + uint32_t cam1_pwdn; + uint32_t cam2_pwdn; + uint32_t cam1_strobe; + uint32_t lcd_te; + uint32_t lcd_bl_pwm; + uint32_t lcd_bl_en; + uint32_t lcd_rst; + uint32_t lcd_gpio1; + uint32_t lcd_gpio2; + uint32_t ap_ready; + uint32_t touch_rst; + uint32_t touch_clk; + uint32_t modem_wake_ap; + uint32_t touch_int; + uint32_t motion_int; + uint32_t als_prox_int; + uint32_t temp_alert; + uint32_t button_power_on; + uint32_t button_vol_up; + uint32_t button_vol_down; + uint32_t button_slide_sw; + uint32_t button_home; + uint32_t pa6; + uint32_t pe6; + uint32_t pe7; + uint32_t ph6; + uint32_t pk0; + uint32_t pk1; + uint32_t pk2; + uint32_t pk3; + uint32_t pk4; + uint32_t pk5; + uint32_t pk6; + uint32_t pk7; + uint32_t pl0; + uint32_t pl1; + uint32_t pz0; + uint32_t pz1; + uint32_t pz2; + uint32_t pz3; + uint32_t pz4; + uint32_t pz5; +}; + +/** + * Constants for use of the Tegra Pinmux. + */ +enum tegra_pinmux_constants { + + /* Tristate (output buffer) control */ + PINMUX_TRISTATE_PASSTHROUGH = (1 << 4), + + /* Input control */ + PINMUX_INPUT = (1 << 6), + + /* Pull resistors */ + PINMUX_PULL_NONE = 0, + PINMUX_PULL_DOWN = 1, + PINMUX_PULL_UP = 2, + + /* Function select */ + PINMUX_SELECT_FUNCTION0 = 0, + PINMUX_SELECT_FUNCTION1 = 1, + PINMUX_SELECT_FUNCTION2 = 2, + PINMUX_SELECT_FUNCTION3 = 3, +}; + + +/** + * Utility function that grabs the Tegra pinmux registers. + */ +static inline struct tegra_pinmux *pinmux_get_regs(void) +{ + return (struct tegra_pinmux *)0x70003000; +} + + +#endif diff --git a/fusee/fusee-primary/src/pmc.h b/fusee/fusee-primary/src/pmc.h index 1596148e5..3acb5e0b7 100644 --- a/fusee/fusee-primary/src/pmc.h +++ b/fusee/fusee-primary/src/pmc.h @@ -1,12 +1,12 @@ -#ifndef FUSEE_PMC_H -#define FUSEE_PMC_H +#ifndef __FUSEE_PMC_H__ +#define __FUSEE_PMC_H__ #include "utils.h" #define PMC_BASE 0x7000E400 - +/* TODO: get rid of these defines; use the struct instead */ #define APBDEV_PMC_CONTROL MAKE_REG32(PMC_BASE + 0x00) #define APBDEV_PMC_DPD_ENABLE_0 MAKE_REG32(PMC_BASE + 0x24) @@ -26,6 +26,292 @@ #define APBDEV_PMC_SCRATCH200_0 MAKE_REG32(PMC_BASE + 0x840) +/** + * Definitions of the Tegra PMC. + */ +struct PACKED tegra_pmc { + uint32_t cntrl; + uint32_t sec_disable; + uint32_t pmc_swrst; + uint32_t wake_mask; + uint32_t wake_lvl; + uint32_t wake_status; + uint32_t sw_wake_status; + uint32_t dpd_pads_oride; + uint32_t dpd_sample; + uint32_t dpd_enable; + uint32_t pwrgate_timer_off; + uint32_t clamp_status; + uint32_t pwrgate_toggle; + uint32_t remove_clamping; + uint32_t pwrgate_status; + uint32_t pwrgood_timer; + uint32_t blink_timer; + uint32_t no_iopower; + uint32_t pwr_det; + uint32_t pwr_det_latch; + + uint32_t scratch0; + uint32_t scratch1; + uint32_t scratch2; + uint32_t scratch3; + uint32_t scratch4; + uint32_t scratch5; + uint32_t scratch6; + uint32_t scratch7; + uint32_t scratch8; + uint32_t scratch9; + uint32_t scratch10; + uint32_t scratch11; + uint32_t scratch12; + uint32_t scratch13; + uint32_t scratch14; + uint32_t scratch15; + uint32_t scratch16; + uint32_t scratch17; + uint32_t scratch18; + uint32_t scratch19; + uint32_t scratch20; + uint32_t scratch21; + uint32_t scratch22; + uint32_t scratch23; + + uint32_t secure_scratch0; + uint32_t secure_scratch1; + uint32_t secure_scratch2; + uint32_t secure_scratch3; + uint32_t secure_scratch4; + uint32_t secure_scratch5; + + uint32_t cpupwrgood_timer; + uint32_t cpupwroff_timer; + uint32_t pg_mask; + uint32_t pg_mask_1; + uint32_t auto_wake_lvl; + uint32_t auto_wake_lvl_mask; + uint32_t wake_delay; + uint32_t pwr_det_val; + uint32_t ddr_pwr; + uint32_t usb_debounce_del; + uint32_t usb_ao; + uint32_t crypto_op; + uint32_t pllp_wb0_override; + + uint32_t scratch24; + uint32_t scratch25; + uint32_t scratch26; + uint32_t scratch27; + uint32_t scratch28; + uint32_t scratch29; + uint32_t scratch30; + uint32_t scratch31; + uint32_t scratch32; + uint32_t scratch33; + uint32_t scratch34; + uint32_t scratch35; + uint32_t scratch36; + uint32_t scratch37; + uint32_t scratch38; + uint32_t scratch39; + uint32_t scratch40; + uint32_t scratch41; + uint32_t scratch42; + + uint32_t bo_mirror0; + uint32_t bo_mirror1; + uint32_t bo_mirror2; + uint32_t sys_33v_en; + uint32_t bo_mirror_access; + uint32_t gate; + uint32_t wake2_mask; + uint32_t wake2_lvl; + uint32_t wake2_stat; + uint32_t sw_wake2_stat; + uint32_t auto_wake2_lvl_mask; + uint32_t pg_mask2; + uint32_t pg_mask_ce1; + uint32_t pg_mask_ce2; + uint32_t pg_mask_ce3; + uint32_t pwrgate_timer_ce0; + uint32_t pwrgate_timer_ce1; + uint32_t pwrgate_timer_ce2; + uint32_t pwrgate_timer_ce3; + uint32_t pwrgate_timer_ce4; + uint32_t pwrgate_timer_ce5; + uint32_t pwrgate_timer_ce6; + uint32_t pcx_edpd_cntrl; + uint32_t osc_edpd_over; + uint32_t clk_out_cntrl; + uint32_t sata_pwrgate; + uint32_t sensor_ctrl; + uint32_t reset_status; + uint32_t io_dpd_req; + uint32_t io_dpd_stat; + uint32_t io_dpd2_req; + uint32_t io_dpd2_stat; + uint32_t sel_dpd_tim; + uint32_t vddp_sel; + + uint32_t ddr_cfg; + uint32_t e_no_vttgen; + uint32_t reserved0; + uint32_t pllm_wb0_ovrride_frq; + uint32_t test_pwrgate; + uint32_t pwrgate_timer_mult; + uint32_t dsi_sel_dpd; + uint32_t utmip_uhsic_triggers; + uint32_t utmip_uhsic_saved_st; + uint32_t utmip_pad_cfg; + uint32_t utmip_term_pad_cfg; + uint32_t utmip_uhsic_sleep_cfg; + + uint32_t todo_0[9]; + uint32_t secure_scratch6; + uint32_t secure_scratch7; + uint32_t scratch43; + uint32_t scratch44; + uint32_t scratch45; + uint32_t scratch46; + uint32_t scratch47; + uint32_t scratch48; + uint32_t scratch49; + uint32_t scratch50; + uint32_t scratch51; + uint32_t scratch52; + uint32_t scratch53; + uint32_t scratch54; + uint32_t scratch55; + uint32_t scratch0_eco; + uint32_t por_dpd_ctrl; + uint32_t scratch2_eco; + uint32_t todo_1[17]; + uint32_t pllm_wb0_override2; + uint32_t tsc_mult; + uint32_t cpu_vsense_override; + uint32_t glb_amap_cfg; + uint32_t sticky_bits; + uint32_t sec_disable2; + uint32_t weak_bias; + uint32_t todo_3[13]; + uint32_t secure_scratch8; + uint32_t secure_scratch9; + uint32_t secure_scratch10; + uint32_t secure_scratch11; + uint32_t secure_scratch12; + uint32_t secure_scratch13; + uint32_t secure_scratch14; + uint32_t secure_scratch15; + uint32_t secure_scratch16; + uint32_t secure_scratch17; + uint32_t secure_scratch18; + uint32_t secure_scratch19; + uint32_t secure_scratch20; + uint32_t secure_scratch21; + uint32_t secure_scratch22; + uint32_t secure_scratch23; + uint32_t secure_scratch24; + uint32_t secure_scratch25; + uint32_t secure_scratch26; + uint32_t secure_scratch27; + uint32_t secure_scratch28; + uint32_t secure_scratch29; + uint32_t secure_scratch30; + uint32_t secure_scratch31; + uint32_t secure_scratch32; + uint32_t secure_scratch33; + uint32_t secure_scratch34; + uint32_t secure_scratch35; + + uint32_t reserved1[52]; + uint32_t cntrl2; + uint32_t reserved2[6]; + uint32_t io_dpd3_req; + uint32_t io_dpd3_stat; + uint32_t strap_opt_a; + uint32_t reserved3[102]; + + uint32_t scratch56; + uint32_t scratch57; + uint32_t scratch58; + uint32_t scratch59; + uint32_t scratch60; + uint32_t scratch61; + uint32_t scratch62; + uint32_t scratch63; + uint32_t scratch64; + uint32_t scratch65; + uint32_t scratch66; + uint32_t scratch67; + uint32_t scratch68; + uint32_t scratch69; + uint32_t scratch70; + uint32_t scratch71; + uint32_t scratch72; + uint32_t scratch73; + uint32_t scratch74; + uint32_t scratch75; + uint32_t scratch76; + uint32_t scratch77; + uint32_t scratch78; + uint32_t scratch79; + uint32_t scratch80; + uint32_t scratch81; + uint32_t scratch82; + uint32_t scratch83; + uint32_t scratch84; + uint32_t scratch85; + uint32_t scratch86; + uint32_t scratch87; + uint32_t scratch88; + uint32_t scratch89; + uint32_t scratch90; + uint32_t scratch91; + uint32_t scratch92; + uint32_t scratch93; + uint32_t scratch94; + uint32_t scratch95; + uint32_t scratch96; + uint32_t scratch97; + uint32_t scratch98; + uint32_t scratch99; + uint32_t scratch100; + uint32_t scratch101; + uint32_t scratch102; + uint32_t scratch103; + uint32_t scratch104; + uint32_t scratch105; + uint32_t scratch106; + uint32_t scratch107; + uint32_t scratch108; + uint32_t scratch109; + uint32_t scratch110; + uint32_t scratch111; + uint32_t scratch112; + uint32_t scratch113; + uint32_t scratch114; + uint32_t scratch115; + uint32_t scratch116; + uint32_t scratch117; + uint32_t scratch118; + uint32_t scratch119; + uint32_t scratch1_eco; +}; + +enum tegra_pmc_masks { + /* NO_IOPOWER, power detect, ect. */ + PMC_CONTROL_SDMMC1 = (1 << 12), + PMC_CONTROL_SDMMC3 = (1 << 13), + PMC_CONTROL_SDMMC4 = (1 << 14), +}; + + +/** + * Utility function that grabs the Tegra pinmux registers. + */ +static inline struct tegra_pmc *pmc_get_regs(void) +{ + return (struct tegra_pmc *)0x7000e400; +} #endif diff --git a/fusee/fusee-primary/src/sdmmc.c b/fusee/fusee-primary/src/sdmmc.c index 28c638bcf..10c0f0eb1 100644 --- a/fusee/fusee-primary/src/sdmmc.c +++ b/fusee/fusee-primary/src/sdmmc.c @@ -1,18 +1,27 @@ +/** + * Fusée SD/MMC driver for the Switch + * ~ktemkin + */ + #include #include #include #include "sdmmc.h" #include "car.h" +#include "pinmux.h" #include "timers.h" #include "apb_misc.h" +#include "gpio.h" +#include "supplies.h" +#include "pmc.h" +#include "pad_control.h" #include "lib/printk.h" #define TEGRA_SDMMC_BASE (0x700B0000) #define TEGRA_SDMMC_SIZE (0x200) - /** * Map of tegra SDMMC registers */ @@ -179,6 +188,10 @@ enum sdmmc_register_bits { /* Host control */ MMC_DMA_SELECT_MASK = (0x3 << 3), + MMC_DMA_SELECT_SDMA = (0x0 << 3), + MMC_HOST_BUS_WIDTH_MASK = (1 << 1) | (1 << 5), + MMC_HOST_BUS_WIDTH_4BIT = (1 << 1), + MMC_HOST_BUS_WIDTH_8BIT = (1 << 5), /* Software reset */ MMC_SOFT_RESET_FULL = (1 << 0), @@ -222,7 +235,7 @@ static const char *sdmmc_command_string[] = { "CMD_TOGGLE_SLEEP_AWAKE", "CMD_SWITCH_MODE", "CMD_TOGGLE_CARD_SELECT", - "CMD_SEND_EXT_CSD", + "CMD_SEND_EXT_CSD/CMD_SEND_IF_COND", "CMD_SEND_CSD", "CMD_SEND_CID ", "", @@ -270,7 +283,7 @@ enum sdmmc_switch_field { /* Fields */ MMC_GROUP_ERASE_DEF = 175, MMC_PARTITION_CONFIG = 179, - + MMC_BUS_WIDTH = 183, }; @@ -278,6 +291,9 @@ enum sdmmc_switch_field { * SDMMC command argument numbers */ enum sdmmc_command_magic { + MMC_ENABLE_BOOT_INIT_MAGIC = 0xf0f0f0f0, + MMC_ENABLE_BOOT_ENABLE_MAGIC = 0xfffffffa, + MMC_EMMC_OPERATING_COND_CAPACITY_MAGIC = 0x00ff8080, MMC_EMMC_OPERATING_COND_CAPACITY_MASK = 0x0fffffff, @@ -290,6 +306,10 @@ enum sdmmc_command_magic { MMC_STATUS_PROGRAMMING = (0x7 << 9), MMC_STATUS_READY_FOR_DATA = (0x1 << 8), MMC_STATUS_CHECK_ERROR = (~0x0206BF7F), + + /* IF_COND components */ + MMC_IF_VOLTAGE_3V3 = (1 << 8), + MMC_IF_CHECK_PATTERN = 0xAA, }; @@ -302,6 +322,7 @@ enum sdmmc_csd_versions { }; + /** * Positions of different fields in various CSDs. * May eventually be replaced with a bitfield struct, if we use enough of the CSDs. @@ -325,8 +346,8 @@ enum sdmmc_ext_csd_extents { MMC_EXT_CSD_SIZE = 512, /* Hardware partition registers */ - MMC_EXT_CSD_PARTITION_SETTING = 155, - MMC_EXT_CSD_PARTITIONING_COMPLETE = (1 << 0), + MMC_EXT_CSD_PARTITION_SETTING_COMPLETE = 155, + MMC_EXT_CSD_PARTITION_SETTING_COMPLETED = (1 << 0), MMC_EXT_CSD_PARTITION_ATTRIBUTE = 156, MMC_EXT_CSD_PARTITION_ENHANCED_ATTRIBUTE = 0x1f, @@ -346,6 +367,9 @@ enum sdmmc_ext_csd_extents { }; + + + /* Forward declarations. */ static int sdmmc_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode mode, enum sdmmc_switch_field field, uint16_t value, uint32_t timeout); @@ -432,26 +456,16 @@ static int sdmmc_hardware_reset(struct mmc *mmc) return 0; } - /** - * Initialize the low-level SDMMC hardware. + * Performs low-level initialization for SDMMC4, used for the eMMC. */ -static int sdmmc_hardware_init(struct mmc *mmc) +static int sdmmc4_hardware_init(struct mmc *mmc) { volatile struct tegra_car *car = car_get_regs(); - volatile struct tegra_sdmmc *regs = mmc->regs; + volatile struct tegra_padctl *padctl = padctl_get_regs(); + (void)mmc; - uint32_t timebase; - bool is_timeout; - - int rc; - - /* XXX fixme XXX */ - bool is_hs400_hs667 = false; - - mmc_print(mmc, "initializing in %s-speed mode...", is_hs400_hs667 ? "high" : "low"); - - // FIXME: set up clock and reset to fetch the relevant clock register offsets + mmc_print(mmc, "enabling eMMC card"); // Put SDMMC4 in reset car->rst_dev_l_set |= 0x8000; @@ -460,7 +474,7 @@ static int sdmmc_hardware_init(struct mmc *mmc) // We use 32 beacuse Nintendo does, and they probably know what they're doing? car->clk_src[CLK_SOURCE_SDMMC4] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; - // Set the legacy divier used for + // Set the legacy divier used for detecting timeouts. car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; // Set SDMMC4 clock enable @@ -472,8 +486,137 @@ static int sdmmc_hardware_init(struct mmc *mmc) // Take SDMMC4 out of reset car->rst_dev_l_clr |= 0x8000; + // Enable input paths for all pins. + padctl->sdmmc2_control |= + PADCTL_SDMMC4_ENABLE_DATA_IN | PADCTL_SDMMC4_ENABLE_CLK_IN | PADCTL_SDMMC4_DEEP_LOOPBACK; + + return 0; +} + + +/** + * Performs low-level initialization for SDMMC1, used for the SD card slot. + */ +static int sdmmc1_hardware_init(struct mmc *mmc) +{ + volatile struct tegra_car *car = car_get_regs(); + volatile struct tegra_pinmux *pinmux = pinmux_get_regs(); + volatile struct tegra_pmc *pmc = pmc_get_regs(); + volatile struct tegra_padctl *padctl = padctl_get_regs(); + (void)mmc; + + // Ensure the PMC is prepared for the SDMMC card to recieve power. + pmc->no_iopower |= PMC_CONTROL_SDMMC1; + pmc->pwr_det_val |= PMC_CONTROL_SDMMC1; + + // Configure the enable line for the SD card power. + pinmux->dmic3_clk = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0; + gpio_configure_mode(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_MODE_GPIO); + gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_OUTPUT); + gpio_write(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_LEVEL_HIGH); + + // Set up each of the relevant pins to be connected to output drivers, + // and selected for SDMMC use. + pinmux->sdmmc1_clk = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + pinmux->sdmmc1_cmd = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + pinmux->sdmmc1_dat3 = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + pinmux->sdmmc1_dat2 = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + pinmux->sdmmc1_dat1 = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + pinmux->sdmmc1_dat0 = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT; + + // Set up the SDMMC write protect. + // TODO: should this be an output, that we control? + pinmux->pz4 = PINMUX_TRISTATE_PASSTHROUGH | PINMUX_SELECT_FUNCTION0 | PINMUX_PULL_UP; + + // Ensure we're using GPIO and not GPIO for the SD card's card detect. + padctl->vgpio_gpio_mux_sel &= ~PADCTL_SDMMC1_CD_SOURCE; + mmc_print(mmc, "mux sel is at %p", &padctl->vgpio_gpio_mux_sel); + + // Set up the card detect pin as a GPIO input. + pinmux->pz1= PINMUX_SELECT_FUNCTION1 | PINMUX_PULL_UP | PINMUX_INPUT; + gpio_configure_mode(GPIO_MICROSD_CARD_DETECT, GPIO_MODE_GPIO); + gpio_configure_direction(GPIO_MICROSD_CARD_DETECT, GPIO_DIRECTION_INPUT); + udelay(100); + + // Set up SD card voltages. + udelay(1000); + supply_enable(SUPPLY_MICROSD); + udelay(1000); + + // Put SDMMC1 in reset + car->rst_dev_l_set |= CAR_CONTROL_SDMMC1; + + // Set SDMMC1 clock source (PLLP_OUT0) and divisor (32). + // We use 32 beacuse Nintendo does, and they probably know what they're doing? + car->clk_src[CLK_SOURCE_SDMMC1] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; + + // Set the legacy divier used for detecting timeouts. + car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; + + // Set SDMMC1 clock enable + car->clk_dev_l_set |= CAR_CONTROL_SDMMC1; + + // host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles + udelay(5000); + + // Take SDMMC4 out of reset + car->rst_dev_l_clr |= CAR_CONTROL_SDMMC1; + + // Enable clock loopback. + padctl->sdmmc1_control |= PADCTL_SDMMC1_DEEP_LOOPBACK; + + return 0; +} + + +/** + * Sets up the I/O and clocking resources necessary to use the given controller. + */ +static int sdmmc_setup_controller_clock_and_io(struct mmc *mmc) +{ + // Always use the per-controller initialization functions. + switch(mmc->controller) { + case SWITCH_MICROSD: + return sdmmc1_hardware_init(mmc); + case SWITCH_EMMC: + return sdmmc4_hardware_init(mmc); + default: + mmc_print(mmc, "initializing an unsupport SDMMC controller!"); + return ENODEV; + } + + return 0; +} + + +/** + * Initialize the low-level SDMMC hardware. + * Thanks to hexkyz for this init code. + * + * FIXME: clean up the magic numbers, split into sections. + */ +static int sdmmc_hardware_init(struct mmc *mmc) +{ + volatile struct tegra_sdmmc *regs = mmc->regs; + + uint32_t timebase; + bool is_timeout; + + int rc; + + // Initialize the Tegra resources necessary to use the given piece of hardware. + rc = sdmmc_setup_controller_clock_and_io(mmc); + if (rc) { + mmc_print(mmc, "ERROR: could not set up controller for use!"); + return rc; + } + + if (!sdmmc_card_present(mmc)) { + mmc_print(mmc, "ERROR: no card detected!"); + return ENODEV; + } + // Software reset the SDMMC device - mmc_print(mmc, "resetting controller..."); rc = sdmmc_hardware_reset(mmc); if (rc) { mmc_print(mmc, "failed to reset!"); @@ -511,8 +654,7 @@ static int sdmmc_hardware_init(struct mmc *mmc) // Set AUTO_CAL_START and AUTO_CAL_ENABLE regs->auto_cal_config |= 0xA0000000; - // Wait one second - udelay(1); + udelay(1000); // Program a timeout of 10ms is_timeout = false; @@ -524,20 +666,26 @@ static int sdmmc_hardware_init(struct mmc *mmc) // Keep checking if timeout expired is_timeout = get_time_since(timebase) > 10000; } - - // AUTO_CAL_ACTIVE was not cleared in time - if (is_timeout) - { - mmc_print(mmc, "autocal timed out!"); - // Set CFG2TMC_EMMC4_PAD_DRVUP_COMP and CFG2TMC_EMMC4_PAD_DRVDN_COMP - APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0x3F00)) | 0x1000); - APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0xFC)) | 0x40); - - // Clear AUTO_CAL_ENABLE - regs->auto_cal_config &= ~(0x20000000); + // AUTO_CAL_ACTIVE was not cleared in time + if (is_timeout) { + mmc_print(mmc, "autocal failed!"); + return ETIMEDOUT; } + // + //if (is_timeout) + //{ + // mmc_print(mmc, "autocal timed out!"); + + // // Set CFG2TMC_EMMC4_PAD_DRVUP_COMP and CFG2TMC_EMMC4_PAD_DRVDN_COMP + // APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0x3F00)) | 0x1000); + // APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0xFC)) | 0x40); + // + // // Clear AUTO_CAL_ENABLE + // regs->auto_cal_config &= ~(0x20000000); + //} + mmc_print(mmc, "autocal complete."); // Clear PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only) @@ -571,7 +719,6 @@ static int sdmmc_hardware_init(struct mmc *mmc) // Clear SDHCI_PROG_CLOCK_MODE regs->clock_control &= ~(0x20); - // Clear SDHCI_CTRL_SDMA and SDHCI_CTRL_ADMA2 regs->host_control &= 0xE7; @@ -589,23 +736,11 @@ static int sdmmc_hardware_init(struct mmc *mmc) regs->power_control |= 0x01; - if (is_hs400_hs667) - { - // Set DQS_TRIM_VAL - regs->vendor_cap_overrides &= ~(0x3F00); - regs->vendor_cap_overrides |= 0x2800; - } - // Clear TAP_VAL_UPDATED_BY_HW regs->vendor_tuning_cntrl0 &= ~(0x20000); - // Software tap value should be 0 for SDMMC4, but HS400/HS667 modes - // must take this value from the tuning procedure - uint32_t tap_value = is_hs400_hs667 ? 1 : 0; - // Set TAP_VAL regs->vendor_clock_cntrl &= ~(0xFF0000); - regs->vendor_clock_cntrl |= (tap_value << 16); // Clear SDHCI_CTRL_HISPD regs->host_control &= 0xFB; @@ -619,51 +754,6 @@ static int sdmmc_hardware_init(struct mmc *mmc) regs->clock_control |= (0x80 << 8); // use the slowest setting, for now //regs->clock_control |= ((sd_divider_lo << 0x08) | (sd_divider_hi << 0x06)); - // HS400/HS667 modes require additional DLL calibration - if (is_hs400_hs667) - { - // Set CALIBRATE - regs->vendor_dllcal_cfg |= 0x80000000; - - // Program a timeout of 5ms - timebase = get_time(); - is_timeout = false; - - // Wait for CALIBRATE to be cleared - mmc_print(mmc, "starting calibration..."); - while(regs->vendor_dllcal_cfg & 0x80000000 && !is_timeout) { - // Keep checking if timeout expired - is_timeout = get_time_since(timebase) > 5000; - } - - // Failed to calibrate in time - if (is_timeout) { - mmc_print(mmc, "calibration failed!"); - return -1; - } - - mmc_print(mmc, "calibration okay."); - - // Program a timeout of 10ms - timebase = get_time(); - is_timeout = false; - - // Wait for DLL_CAL_ACTIVE to be cleared - mmc_print(mmc, "waiting for calibration to finalize.... "); - while((regs->vendor_dllcal_cfg_sta & 0x80000000) && !is_timeout) { - // Keep checking if timeout expired - is_timeout = get_time_since(timebase) > 10000; - } - - // Failed to calibrate in time - if (is_timeout) { - mmc_print(mmc, "calibration failed to finalize!"); - return -1; - } - - mmc_print(mmc, "calibration complete!"); - } - // Set SDHCI_CLOCK_CARD_EN regs->clock_control |= 0x04; @@ -1126,7 +1216,7 @@ static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command, // Wait for the command to be completed. rc = sdmmc_wait_for_command_completion(mmc); if (rc) { - mmc_print(mmc, "failed to issue CMD%d (arg=%08x, rc=%d)", command, argument, rc); + mmc_print(mmc, "failed to issue %s (arg=%08x, rc=%d)", sdmmc_command_string[command], argument, rc); mmc_print_command_errors(mmc, rc); sdmmc_enable_interrupts(mmc, false); @@ -1202,55 +1292,6 @@ static int sdmmc_send_simple_command(struct mmc *mmc, enum sdmmc_command command } -/** - * Handles eMMC-specific card initialization. - */ -static int emmc_card_init(struct mmc *mmc) -{ - int rc; - uint32_t response[4]; - - mmc_print(mmc, "setting up card as eMMC"); - - // We only support Switch eMMC addressing, which is alawys block-based. - mmc->uses_block_addressing = true; - - // Bring the bus out of its idle state. - rc = sdmmc_send_simple_command(mmc, CMD_GO_IDLE_OR_INIT, MMC_RESPONSE_NONE, 0, NULL); - if (rc) { - mmc_print(mmc, "could not bring bus to idle!"); - return rc; - } - - // Wait for the card to finish being busy. - while (true) { - - uint32_t response_masked; - - // Ask the SD card to identify its state. It will respond with readiness and a capacity magic. - rc = sdmmc_send_command(mmc, CMD_SEND_OPERATING_CONDITIONS, MMC_RESPONSE_LEN48, - MMC_CHECKS_NONE, 0x40000080, response, 0, false, false, NULL); - if (rc) { - mmc_print(mmc, "ERROR: could not read the card's operating conditions!"); - return rc; - } - - // Validate that this is a valid Switch eMMC. - // Per the spec, any card greater than 2GiB should respond with this magic number. - response_masked = response[0] & MMC_EMMC_OPERATING_COND_CAPACITY_MASK; - if (response_masked != MMC_EMMC_OPERATING_COND_CAPACITY_MAGIC) { - mmc_print(mmc, "ERROR: this doesn't appear to be a valid Switch eMMC!"); - return ENOTTY; - } - - // If the device has just become ready, we're done! - response_masked = response[0] & MMC_EMMC_OPERATING_READINESS_MASK; - if (response_masked == MMC_EMMC_OPERATING_COND_READY) { - return 0; - } - } -} - /** * Reads a collection of bits from the CSD register. @@ -1360,6 +1401,7 @@ static int sdmmc_read_and_parse_csd(struct mmc *mmc) } + /** * Reads the active MMC card's Card Specific Data, and updates the MMC object's properties. * @@ -1387,7 +1429,7 @@ static int sdmmc_read_and_parse_ext_csd(struct mmc *mmc) mmc->partition_support = ext_csd[MMC_EXT_CSD_PARTITION_SUPPORT]; mmc->partition_config = ext_csd[MMC_EXT_CSD_PARTITION_CONFIG] & ~MMC_EXT_CSD_PARTITION_SELECT_MASK; mmc->partition_switch_time = ext_csd[MMC_EXT_CSD_PARTITION_SWITCH_TIME] * MMC_EXT_CSD_PARTITION_SWITCH_SCALE_US; - mmc->partition_setting = ext_csd[MMC_EXT_CSD_PARTITION_SETTING]; + mmc->partitioned = ext_csd[MMC_EXT_CSD_PARTITION_SETTING_COMPLETE] & MMC_EXT_CSD_PARTITION_SETTING_COMPLETED; mmc->partition_attribute = ext_csd[MMC_EXT_CSD_PARTITION_ATTRIBUTE]; return 0; @@ -1416,12 +1458,54 @@ static int sdmmc_set_up_block_transfer_size(struct mmc *mmc) return 0; } +/** + * Switches the SDMMC card and controller to the fullest bus width possible. + * + * @param mmc The MMC controller to switch up to a full bus width. + */ +static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width) +{ + // Ask the card to adjust to the wider bus width. + int rc = sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, + MMC_BUS_WIDTH, width, mmc->timeout); + if (rc) { + mmc_print(mmc, "could not switch mode on the card side!"); + return rc; + } + + // And switch the bus width on our side. + mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK; + switch(width) { + case MMC_BUS_WIDTH_4BIT: + mmc->regs->host_control |= MMC_HOST_BUS_WIDTH_4BIT; + break; + case MMC_BUS_WIDTH_8BIT: + mmc->regs->host_control |= MMC_HOST_BUS_WIDTH_8BIT; + break; + default: + break; + } + + return 0; +} + + /** * Optimize our SDMMC transfer mode to fully utilize the bus. */ -static int sdmmc_optimize_transfer_mode(struct mmc *mmc) +static int mmc_optimize_transfer_mode(struct mmc *mmc) { - // FIXME: use this to setup higher data widths + int rc; + + // Switch the device to its maximum bus width. + rc = sdmmc_switch_bus_width(mmc, mmc->max_bus_width); + if (rc) { + mmc_print(mmc, "could not switch the controller's bus width!"); + return rc; + } + + // TODO: step up into high speed modes + mmc_print(mmc, "now operating with a wider bus width"); return 0; } @@ -1432,19 +1516,18 @@ static int sdmmc_optimize_transfer_mode(struct mmc *mmc) */ static int sdmmc_set_up_partitions(struct mmc *mmc) { - bool partitions_exist = mmc->partition_setting & MMC_EXT_CSD_PARTITIONING_COMPLETE; - bool has_enhanced_attributes = mmc->partition_attribute & MMC_EXT_CSD_PARTITION_ENHANCED_ATTRIBUTE; - // If the card doesn't support partitions, fail out. if (!(mmc->partition_support & MMC_SUPPORTS_HARDWARE_PARTS)) return ENOTTY; // If the card hasn't been partitioned, fail out. // We don't support setting up hardware partitioning. - if (!partitions_exist || !has_enhanced_attributes) + if (!mmc->partitioned) { + mmc_print(mmc, "NOTE: card supports partitions but is not partitioned"); return ENOTDIR; + } - mmc_print(mmc, "detected a card with hardware (boot) partitions."); + mmc_print(mmc, "detected a card with hardware partitions."); // Use partitioning. return sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, @@ -1452,6 +1535,9 @@ static int sdmmc_set_up_partitions(struct mmc *mmc) } + + + /** * Retrieves information about the card, and populates the MMC structure accordingly. * Used as part of the SDMMC initialization process. @@ -1499,11 +1585,90 @@ static int sdmmc_card_init(struct mmc *mmc) return EPIPE; } + return 0; +} + + +/** + * Blocks until the eMMC card is fully initialized. + * + * @param mmc The MMC device that should do the waiting. + */ +static int sdmmc_wait_for_card_readiness(struct mmc *mmc) +{ + int rc; + uint32_t response[4]; + + while (true) { + + uint32_t response_masked; + + // Ask the SD card to identify its state. It will respond with readiness and a capacity magic. + rc = sdmmc_send_command(mmc, CMD_SEND_OPERATING_CONDITIONS, MMC_RESPONSE_LEN48, + MMC_CHECKS_NONE, 0x40000080, response, 0, false, false, NULL); + if (rc) { + mmc_print(mmc, "ERROR: could not read the card's operating conditions!"); + return rc; + } + + // Validate that this is a valid Switch eMMC. + // Per the spec, any card greater than 2GiB should respond with this magic number. + response_masked = response[0] & MMC_EMMC_OPERATING_COND_CAPACITY_MASK; + if (response_masked != MMC_EMMC_OPERATING_COND_CAPACITY_MAGIC) { + mmc_print(mmc, "ERROR: this doesn't appear to be a valid Switch eMMC!"); + return ENOTTY; + } + + // If the device has just become ready, we're done! + response_masked = response[0] & MMC_EMMC_OPERATING_READINESS_MASK; + if (response_masked == MMC_EMMC_OPERATING_COND_READY) { + return 0; + } + } +} + + +/** + * Handles MMC-specific card initialization. + */ +static int sdmmc_mmc_card_init(struct mmc *mmc) +{ + int rc; + + mmc_print(mmc, "setting up card as MMC"); + + // We only support Switch eMMC addressing, which is alawys block-based. + mmc->uses_block_addressing = true; + + // Bring the bus out of its idle state. + rc = sdmmc_send_simple_command(mmc, CMD_GO_IDLE_OR_INIT, MMC_RESPONSE_NONE, 0, NULL); + if (rc) { + mmc_print(mmc, "could not bring bus to idle!"); + return rc; + } + + // Wait for the card to finish being busy. + rc = sdmmc_wait_for_card_readiness(mmc); + if (rc) { + mmc_print(mmc, "card failed to come up! (%d)", rc); + return rc; + } + + // Run the common core card initialization. + rc = sdmmc_card_init(mmc); + if (rc) { + mmc_print(mmc, "failed to set up card (%d)!", rc); + return rc; + } + // Switch to a transfer mode that can more efficiently utilize the bus. - rc = sdmmc_optimize_transfer_mode(mmc); + /* + rc = mmc_optimize_transfer_mode(mmc); if (rc) { mmc_print(mmc, "could not optimize bus utlization! (%d)", rc); } + */ + (void)mmc_optimize_transfer_mode; // Read and handle card's Extended Card Specific Data (ext-CSD). rc = sdmmc_read_and_parse_ext_csd(mmc); @@ -1515,13 +1680,75 @@ static int sdmmc_card_init(struct mmc *mmc) // Set up MMC card partitioning, if supported. rc = sdmmc_set_up_partitions(mmc); if (rc) { - mmc_print(mmc, "NOTE: card cannot be used with hardware partitions (%d)", rc); + mmc_print(mmc, "NOTE: card cannot be used with hardware partitions", rc); } return 0; } +/** + * Evalutes a check pattern response (used with interface commands) + * and validates that it contains our common check pattern. + * + * @param response The response recieved after a given command. + * @return True iff the given response has a valid check pattern. + */ +static bool sdmmc_check_pattern_present(uint32_t response) +{ + uint32_t pattern_byte = response & 0xFF; + return pattern_byte == MMC_IF_CHECK_PATTERN; +} + + + +/** + * Handles SD-specific card initialization. + */ +static int sdmmc_sd_card_init(struct mmc *mmc) +{ + int rc; + uint32_t response; + + mmc_print(mmc, "setting up card as SD"); + + // Bring the bus out of its idle state. + rc = sdmmc_send_simple_command(mmc, CMD_GO_IDLE_OR_INIT, MMC_RESPONSE_NONE, 0, NULL); + if (rc) { + mmc_print(mmc, "could not bring bus to idle!"); + return rc; + } + + // Validate that the card can handle working with the voltages we can provide. + rc = sdmmc_send_simple_command(mmc, CMD_SEND_IF_COND, MMC_RESPONSE_LEN48, MMC_IF_VOLTAGE_3V3 | MMC_IF_CHECK_PATTERN, &response); + if (rc || !sdmmc_check_pattern_present(response)) { + // TODO: Maybe don't assume we _need_ 3V3 interfacing? + mmc_print(mmc, "card can't talk at our voltage (rc=%d, check=%02x)!", rc, response & 0xFF); + return rc; + } + + // Wait for the card to finish being busy. + rc = sdmmc_wait_for_card_readiness(mmc); + if (rc) { + mmc_print(mmc, "card failed to come up! (%d)", rc); + return rc; + } + + // Run the common core card initialization. + rc = sdmmc_card_init(mmc); + if (rc) { + mmc_print(mmc, "failed to set up card (%d)!", rc); + return rc; + } + + // FIXME: optimize bus utilization here? + // is this just a call to the same routine as for eMMC? + return 0; +} + + + + /** * Handle any speciailized initialization required by the given device type. * @@ -1536,7 +1763,12 @@ static int sdmmc_handle_card_type_init(struct mmc *mmc) // Handle initialization of eMMC cards. case MMC_CARD_EMMC: // FIXME: also handle MMC and SD cards that aren't eMMC - rc = emmc_card_init(mmc); + rc = sdmmc_mmc_card_init(mmc); + break; + + // Handle initialization of SD. + case MMC_CARD_SD: + rc = sdmmc_sd_card_init(mmc); break; default: @@ -1550,55 +1782,7 @@ static int sdmmc_handle_card_type_init(struct mmc *mmc) -/** - * Set up a new SDMMC driver. - * FIXME: clean up! - * - * @param mmc The SDMMC structure to be initiailized with the device state. - * @param controler The controller description to be used; usually SWITCH_EMMC - * or SWTICH_MICROSD. - */ -int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) -{ - int rc; - // Get a reference to the registers for the relevant SDMMC controller. - mmc->regs = sdmmc_get_regs(controller); - mmc->name = "eMMC"; - mmc->card_type = MMC_CARD_EMMC; - - // Default to a timeout of 1S. - mmc->timeout = 1000000; - - // Use DMA, by default. - mmc->use_dma = true; - - // Default to relative address of zero. - mmc->relative_address = 0; - - // Initialize the raw SDMMC controller. - rc = sdmmc_hardware_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set up controller! (%d)", rc); - return rc; - } - - // Handle the initialization that's specific to the card type. - rc = sdmmc_handle_card_type_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set run card-specific initialization (%d)!", rc); - return rc; - } - - // Handle the initialization that's common to all card types. - rc = sdmmc_card_init(mmc); - if (rc) { - mmc_print(mmc, "failed to set up card (%d)!", rc); - return rc; - } - - return 0; -} /** @@ -1679,10 +1863,12 @@ static int sdmmc_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode mode } // Wait until we have a sense of the card status to return. - rc = sdmmc_wait_for_card_ready(mmc, timeout); - if (rc){ - mmc_print(mmc, "failed to talk to the card after SWITCH_MODE (%d)", rc); - return rc; + if(timeout != 0) { + rc = sdmmc_wait_for_card_ready(mmc, timeout); + if (rc){ + mmc_print(mmc, "failed to talk to the card after SWITCH_MODE (%d)", rc); + return rc; + } } return 0; @@ -1698,6 +1884,78 @@ static bool sdmmc_supports_hardware_partitions(struct mmc *mmc) } +/** + * Populates the given MMC object with defaults for its controller. + * + * @param mmc The mmc object to populate. + */ +static void sdmmc_initialize_defaults(struct mmc *mmc) +{ + // Set up based on the controller + switch(mmc->controller) { + case SWITCH_EMMC: + mmc->name = "eMMC"; + mmc->card_type = MMC_CARD_EMMC; + mmc->max_bus_width = MMC_BUS_WIDTH_8BIT; + break; + + case SWITCH_MICROSD: + mmc->name = "uSD"; + mmc->card_type = MMC_CARD_SD; + mmc->max_bus_width = MMC_BUS_WIDTH_4BIT; + break; + + default: + printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller); + break; + } +} + + +/** + * Set up a new SDMMC driver. + * FIXME: clean up! + * + * @param mmc The SDMMC structure to be initiailized with the device state. + * @param controler The controller description to be used; usually SWITCH_EMMC + * or SWTICH_MICROSD. + */ +int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) +{ + int rc; + + // Get a reference to the registers for the relevant SDMMC controller. + mmc->controller = controller; + mmc->regs = sdmmc_get_regs(controller); + sdmmc_initialize_defaults(mmc); + + // Default to a timeout of 1S. + mmc->timeout = 1000000; + mmc->partition_switch_time = 1000; + + // Use DMA, by default. + mmc->use_dma = true; + + // Default to relative address of zero. + mmc->relative_address = 0; + + // Initialize the raw SDMMC controller. + rc = sdmmc_hardware_init(mmc); + if (rc) { + mmc_print(mmc, "failed to set up controller! (%d)", rc); + return rc; + } + + // Handle the initialization that's specific to the card type. + rc = sdmmc_handle_card_type_init(mmc); + if (rc) { + mmc_print(mmc, "failed to set run card-specific initialization (%d)!", rc); + return rc; + } + + return 0; +} + /** * Selects the active MMC partition. Can be used to select @@ -1710,7 +1968,7 @@ static bool sdmmc_supports_hardware_partitions(struct mmc *mmc) */ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition) { - uint16_t argument = mmc->partition_config | partition; + uint16_t argument = partition; int rc; // If we're trying to access hardware partitions on a device that doesn't support them, @@ -1720,11 +1978,14 @@ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition) // Set the PARTITION_CONFIG register to select the active partition. mmc_print(mmc, "switching to partition %d", partition); - rc = sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, MMC_PARTITION_CONFIG, argument, mmc->partition_switch_time); + rc = sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, MMC_PARTITION_CONFIG, argument, 0); if (rc) { mmc_print(mmc, "failed to select partition %d (%02x, rc=%d)", partition, argument, rc); } + mmc_print(mmc, "waiting for %d us", mmc->partition_switch_time); + udelay(mmc->partition_switch_time); + return rc; } @@ -1756,3 +2017,28 @@ int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t block, unsigned int count // Execute the relevant read. return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, false, true, buffer); } + + +/** + * Checks to see whether an SD card is present. + * + * @mmc mmc The controller with which to check for card presence. + * @return true iff a card is present + */ +bool sdmmc_card_present(struct mmc *mmc) +{ + switch (mmc->controller) { + + // The eMMC is always present. + case SWITCH_EMMC: + return true; + + // The Switch's microSD card has a GPIO card detect pin. + case SWITCH_MICROSD: + return !gpio_read(GPIO_MICROSD_CARD_DETECT); + + default: + mmc_print(mmc, "cannot figure out how to determine card presence!"); + return false; + } +} diff --git a/fusee/fusee-primary/src/sdmmc.h b/fusee/fusee-primary/src/sdmmc.h index d1128dc99..0f222ad9f 100644 --- a/fusee/fusee-primary/src/sdmmc.h +++ b/fusee/fusee-primary/src/sdmmc.h @@ -1,3 +1,8 @@ +/** + * Fusée SD/MMC driver for the Switch + * ~ktemkin + */ + #ifndef __FUSEE_SDMMC_H__ #define __FUSEE_SDMMC_H__ @@ -5,10 +10,20 @@ #include #include "utils.h" - /* Opaque pointer to the Tegra SDMMC registers */ struct tegra_sdmmc; + +/** + * Bus widths supported by the SD/MMC cards. + */ +enum sdmmc_bus_width { + MMC_BUS_WIDTH_1BIT = 0, + MMC_BUS_WIDTH_4BIT = 1, + MMC_BUS_WIDTH_8BIT = 2, +}; + + /** * Represents the different types of devices an MMC object * can represent. @@ -20,23 +35,37 @@ enum mmc_card_type { MMC_CARD_CART, }; + +/** + * SDMMC controllers + */ +enum sdmmc_controller { + SWITCH_MICROSD = 0, + SWITCH_EMMC = 3 +}; + + /** * Primary data structure describing a Fusée MMC driver. */ struct mmc { + enum sdmmc_controller controller; + /* Controller properties */ char *name; unsigned int timeout; enum mmc_card_type card_type; bool use_dma; + /* Card properties */ uint8_t cid[15]; uint32_t relative_address; + uint8_t partitioned; + enum sdmmc_bus_width max_bus_width; uint8_t partition_support; uint8_t partition_config; - uint8_t partition_setting; uint8_t partition_attribute; uint32_t partition_switch_time; @@ -48,13 +77,12 @@ struct mmc { }; + + /** - * SDMMC controllers + * Primary data structure describing a Fusée MMC driver. */ -enum sdmmc_controller { - SWITCH_MICROSD = 0, - SWITCH_EMMC = 3 -}; +struct mmc; /** @@ -99,4 +127,12 @@ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition); int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t sector, unsigned int count); +/** + * Checks to see whether an SD card is present. + * + * @mmc mmc The controller with which to check for card presence. + * @return true iff a card is present + */ +bool sdmmc_card_present(struct mmc *mmc); + #endif diff --git a/fusee/fusee-primary/src/supplies.c b/fusee/fusee-primary/src/supplies.c new file mode 100644 index 000000000..821267f44 --- /dev/null +++ b/fusee/fusee-primary/src/supplies.c @@ -0,0 +1,30 @@ +/** + * Fusée power supply control code + * ~ktemkin + */ + +#include "supplies.h" +#include "lib/printk.h" + +// FIXME: replace hwinit with our own code +#include "hwinit/max7762x.h" + +/** + * Enables a given power supply. + * + * @param supply The power domain on the Switch that is to be enabled. + */ +void supply_enable(enum switch_power_supply supply) +{ + switch(supply) { + case SUPPLY_MICROSD: + max77620_regulator_set_voltage(SUPPLY_MICROSD_REGULATOR, SUPPLY_MICROSD_VOLTAGE); + max77620_regulator_enable(SUPPLY_MICROSD_REGULATOR, true); + return; + + default: + printk("ERROR: could not enable unknown supply %d!\n", supply); + return; + } +} + diff --git a/fusee/fusee-primary/src/supplies.h b/fusee/fusee-primary/src/supplies.h new file mode 100644 index 000000000..6a475bd5d --- /dev/null +++ b/fusee/fusee-primary/src/supplies.h @@ -0,0 +1,31 @@ +/** + * Fusée power supply control code + * ~ktemkin + */ + +#ifndef __FUSEE_SUPPLIES_H__ +#define __FUSEE_SUPPLIES_H__ + +#include "utils.h" + +enum switch_power_supply { + SUPPLY_MICROSD, +}; + + +enum switch_power_constants { + + /* MicroSD card */ + SUPPLY_MICROSD_REGULATOR = 6, + SUPPLY_MICROSD_VOLTAGE = 3300000, + +}; + +/** + * Enables a given power supply. + * + * @param supply The power domain on the Switch that is to be enabled. + */ +void supply_enable(enum switch_power_supply supply); + +#endif