mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-28 05:06:08 +00:00
fusee: merge in most of the microSD card (not fully working)
This commit is contained in:
parent
608d59c229
commit
fc97c3f773
13 changed files with 1908 additions and 230 deletions
|
@ -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 */
|
||||
|
|
149
fusee/fusee-primary/src/gpio.c
Normal file
149
fusee/fusee-primary/src/gpio.c
Normal 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));
|
||||
}
|
174
fusee/fusee-primary/src/gpio.h
Normal file
174
fusee/fusee-primary/src/gpio.h
Normal 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
|
|
@ -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_ */
|
||||
|
|
136
fusee/fusee-primary/src/hwinit/max7762x.c
Executable file
136
fusee/fusee-primary/src/hwinit/max7762x.c
Executable 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);
|
||||
}
|
68
fusee/fusee-primary/src/hwinit/max7762x.h
Executable file
68
fusee/fusee-primary/src/hwinit/max7762x.h
Executable 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
|
67
fusee/fusee-primary/src/pad_control.h
Normal file
67
fusee/fusee-primary/src/pad_control.h
Normal 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
|
212
fusee/fusee-primary/src/pinmux.h
Normal file
212
fusee/fusee-primary/src/pinmux.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -526,18 +668,24 @@ static int sdmmc_hardware_init(struct mmc *mmc)
|
|||
}
|
||||
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
30
fusee/fusee-primary/src/supplies.c
Normal file
30
fusee/fusee-primary/src/supplies.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
31
fusee/fusee-primary/src/supplies.h
Normal file
31
fusee/fusee-primary/src/supplies.h
Normal 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
|
Loading…
Reference in a new issue