1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-05 19:51:45 +00:00

fusee: merge in most of the microSD card (not fully working)

This commit is contained in:
Kate J. Temkin 2018-05-02 06:07:21 -06:00
parent 608d59c229
commit fc97c3f773
13 changed files with 1908 additions and 230 deletions

View file

@ -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 */

View file

@ -0,0 +1,149 @@
#include <string.h>
#include <stdint.h>
#include <errno.h>
#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));
}

View file

@ -0,0 +1,174 @@
/*
* Struct defintiions lifted from NVIDIA sample code.
* (C) Copyright 2013-2015 NVIDIA Corporation <www.nvidia.com>
*
* adapted for Fusée by Kate Temkin <k@ktemkin.com.
*/
#ifndef __FUSEE_GPIO_H__
#define __FUSEE_GPIO_H__
#include <stdbool.h>
#include <stdint.h>
#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

View file

@ -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_ */

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -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

View file

@ -0,0 +1,212 @@
#ifndef __FUSEE_PINMUX_H__
#define __FUSEE_PINMUX_H__
#include <stdbool.h>
#include <stdint.h>
#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

View file

@ -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

View file

@ -1,18 +1,27 @@
/**
* Fusée SD/MMC driver for the Switch
* ~ktemkin
*/
#include <string.h>
#include <stdint.h>
#include <errno.h>
#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 ",
"<unsupported>",
@ -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;
}
}

View file

@ -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 <stdint.h>
#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

View file

@ -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;
}
}

View file

@ -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