diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/bcm2835_sdhci.c | 189 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 16 | ||||
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 8 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 8 | ||||
-rw-r--r-- | drivers/mtd/nand/omap_gpmc.c | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/winbond.c | 5 | ||||
-rw-r--r-- | drivers/net/cpsw.c | 1 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/exynos-tmu.c | 319 | ||||
-rw-r--r-- | drivers/serial/ns16550.c | 5 | ||||
-rw-r--r-- | drivers/sound/Makefile | 1 | ||||
-rw-r--r-- | drivers/sound/max98095.c | 550 | ||||
-rw-r--r-- | drivers/sound/max98095.h | 311 | ||||
-rw-r--r-- | drivers/sound/sound.c | 9 | ||||
-rw-r--r-- | drivers/spi/Makefile | 6 | ||||
-rw-r--r-- | drivers/spi/fdt_spi.c | 186 | ||||
-rw-r--r-- | drivers/spi/tegra114_spi.c | 405 | ||||
-rw-r--r-- | drivers/spi/tegra20_sflash.c (renamed from drivers/spi/tegra_spi.c) | 217 | ||||
-rw-r--r-- | drivers/spi/tegra20_slink.c (renamed from drivers/spi/tegra_slink.c) | 128 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/bcm2835.c | 127 |
22 files changed, 2332 insertions, 163 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 65791aa..1d6faa2 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c new file mode 100644 index 0000000..b0afc3c --- /dev/null +++ b/drivers/mmc/bcm2835_sdhci.c @@ -0,0 +1,189 @@ +/* + * This code was extracted from: + * git://github.com/gonzoua/u-boot-pi.git master + * and hence presumably (C) 2012 Oleksandr Tymoshenko + * + * Tweaks for U-Boot upstreaming + * (C) 2012 Stephen Warren + * + * Portions (e.g. read/write macros, concepts for back-to-back register write + * timing workarounds) obviously extracted from the Linux kernel at: + * https://github.com/raspberrypi/linux.git rpi-3.6.y + * + * The Linux kernel code has the following (c) and license, which is hence + * propagated to Oleksandr's tree and here: + * + * Support for SDHCI device on 2835 + * Based on sdhci-bcm2708.c (c) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform device - Arasan SD controller in BCM2708 + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include <common.h> +#include <malloc.h> +#include <sdhci.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define MIN_FREQ 400000 + +struct bcm2835_sdhci_host { + struct sdhci_host host; + uint twoticks_delay; + ulong last_write; +}; + +static inline struct bcm2835_sdhci_host *to_bcm(struct sdhci_host *host) +{ + return (struct bcm2835_sdhci_host *)host; +} + +static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val, + int reg) +{ + struct bcm2835_sdhci_host *bcm_host = to_bcm(host); + + /* + * The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). + * It seems, however, that the data register does not have this problem. + * (Which is just as well - otherwise we'd have to nobble the DMA engine + * too) + */ + while (get_timer(bcm_host->last_write) < bcm_host->twoticks_delay) + ; + + writel(val, host->ioaddr + reg); + bcm_host->last_write = get_timer(0); +} + +static inline u32 bcm2835_sdhci_raw_readl(struct sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + bcm2835_sdhci_raw_writel(host, val, reg); +} + +static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + static u32 shadow; + u32 oldval = (reg == SDHCI_COMMAND) ? shadow : + bcm2835_sdhci_raw_readl(host, reg & ~3); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 mask = 0xffff << word_shift; + u32 newval = (oldval & ~mask) | (val << word_shift); + + if (reg == SDHCI_TRANSFER_MODE) + shadow = newval; + else + bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 oldval = bcm2835_sdhci_raw_readl(host, reg & ~3); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 mask = 0xff << byte_shift; + u32 newval = (oldval & ~mask) | (val << byte_shift); + + bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, reg); + + return val; +} + +static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 word = (val >> word_shift) & 0xffff; + + return word; +} + +static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 byte = (val >> byte_shift) & 0xff; + + return byte; +} + +static const struct sdhci_ops bcm2835_ops = { + .write_l = bcm2835_sdhci_writel, + .write_w = bcm2835_sdhci_writew, + .write_b = bcm2835_sdhci_writeb, + .read_l = bcm2835_sdhci_readl, + .read_w = bcm2835_sdhci_readw, + .read_b = bcm2835_sdhci_readb, +}; + +int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq) +{ + struct bcm2835_sdhci_host *bcm_host; + struct sdhci_host *host; + + bcm_host = malloc(sizeof(*bcm_host)); + if (!bcm_host) { + printf("sdhci_host malloc fail!\n"); + return 1; + } + + /* + * See the comments in bcm2835_sdhci_raw_writel(). + * + * This should probably be dynamically calculated based on the actual + * frequency. However, this is the longest we'll have to wait, and + * doesn't seem to slow access down too much, so the added complexity + * doesn't seem worth it for now. + * + * 1/MIN_FREQ is (max) time per tick of eMMC clock. + * 2/MIN_FREQ is time for two ticks. + * Multiply by 1000000 to get uS per two ticks. + * +1 for hack rounding. + */ + bcm_host->twoticks_delay = ((2 * 1000000) / MIN_FREQ) + 1; + bcm_host->last_write = 0; + + host = &bcm_host->host; + host->name = "bcm2835_sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | + SDHCI_QUIRK_WAIT_SEND_CMD; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + host->ops = &bcm2835_ops; + + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + add_sdhci(host, emmc_freq, MIN_FREQ); + + return 0; +} diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 7b5fdd9..d732581 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -51,8 +51,12 @@ int mmc_getwp(struct mmc *mmc) wp = board_mmc_getwp(mmc); - if ((wp < 0) && mmc->getwp) - wp = mmc->getwp(mmc); + if (wp < 0) { + if (mmc->getwp) + wp = mmc->getwp(mmc); + else + wp = 0; + } return wp; } @@ -692,8 +696,12 @@ int mmc_getcd(struct mmc *mmc) cd = board_mmc_getcd(mmc); - if ((cd < 0) && mmc->getcd) - cd = mmc->getcd(mmc); + if (cd < 0) { + if (mmc->getcd) + cd = mmc->getcd(mmc); + else + cd = 1; + } return cd; } diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 67cfcc2..166744c 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -593,8 +593,6 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, mmc->send_cmd = mmc_send_cmd; mmc->set_ios = mmc_set_ios; mmc->init = mmc_init_setup; - mmc->getcd = omap_mmc_getcd; - mmc->getwp = omap_mmc_getwp; mmc->priv = priv_data; switch (dev_index) { @@ -616,7 +614,13 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, return 1; } priv_data->cd_gpio = omap_mmc_setup_gpio_in(cd_gpio, "mmc_cd"); + if (priv_data->cd_gpio != -1) + mmc->getcd = omap_mmc_getcd; + priv_data->wp_gpio = omap_mmc_setup_gpio_in(wp_gpio, "mmc_wp"); + if (priv_data->wp_gpio != -1) + mmc->getwp = omap_mmc_getwp; + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC) & ~host_caps_mask; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index daca0ea..1eaea04 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -412,9 +412,11 @@ int sdhci_init(struct mmc *mmc) status = sdhci_readl(host, SDHCI_PRESENT_STATE); } - /* Eable all state */ - sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_ENABLE); - sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_SIGNAL_ENABLE); + /* Enable only interrupts served by the SD controller */ + sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK + , SDHCI_INT_ENABLE); + /* Mask all sdhci interrupt sources */ + sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); return 0; } diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index cee394e..bbf5443 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -25,6 +25,7 @@ #include <asm/io.h> #include <asm/errno.h> #include <asm/arch/mem.h> +#include <asm/arch/cpu.h> #include <asm/arch/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> #include <linux/compiler.h> diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index 05dc644..2716209 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -68,6 +68,11 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .name = "W25Q80", }, { + .id = 0x6016, + .nr_blocks = 512, + .name = "W25Q32DW", + }, + { .id = 0x6017, .nr_blocks = 128, .name = "W25Q64DW", diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 93f8417..7a36850 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -24,6 +24,7 @@ #include <asm/errno.h> #include <asm/io.h> #include <phy.h> +#include <asm/arch/cpu.h> #define BITMASK(bits) (BIT(bits) - 1) #define PHY_REG_MASK 0x1f diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 8c71901..1dac16a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libpower.o +COBJS-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o COBJS-$(CONFIG_FTPMU010_POWER) += ftpmu010.o COBJS-$(CONFIG_TPS6586X_POWER) += tps6586x.o COBJS-$(CONFIG_TWL4030_POWER) += twl4030.o diff --git a/drivers/power/exynos-tmu.c b/drivers/power/exynos-tmu.c new file mode 100644 index 0000000..d4b3e65 --- /dev/null +++ b/drivers/power/exynos-tmu.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Akshay Saraswat <akshay.s@samsung.com> + * + * EXYNOS - Thermal Management Unit + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <tmu.h> +#include <asm/arch/tmu.h> +#include <asm/arch/power.h> + +#define TRIMINFO_RELOAD 1 +#define CORE_EN 1 +#define THERM_TRIP_EN (1 << 12) + +#define INTEN_RISE0 1 +#define INTEN_RISE1 (1 << 4) +#define INTEN_RISE2 (1 << 8) +#define INTEN_FALL0 (1 << 16) +#define INTEN_FALL1 (1 << 20) +#define INTEN_FALL2 (1 << 24) + +#define TRIM_INFO_MASK 0xff + +#define INTCLEAR_RISE0 1 +#define INTCLEAR_RISE1 (1 << 4) +#define INTCLEAR_RISE2 (1 << 8) +#define INTCLEAR_FALL0 (1 << 16) +#define INTCLEAR_FALL1 (1 << 20) +#define INTCLEAR_FALL2 (1 << 24) +#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ + INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ + INTCLEAR_FALL1 | INTCLEAR_FALL2) + +/* Tmeperature threshold values for various thermal events */ +struct temperature_params { + /* minimum value in temperature code range */ + unsigned int min_val; + /* maximum value in temperature code range */ + unsigned int max_val; + /* temperature threshold to start warning */ + unsigned int start_warning; + /* temperature threshold CPU tripping */ + unsigned int start_tripping; + /* temperature threshold for HW tripping */ + unsigned int hardware_tripping; +}; + +/* Pre-defined values and thresholds for calibration of current temperature */ +struct tmu_data { + /* pre-defined temperature thresholds */ + struct temperature_params ts; + /* pre-defined efuse range minimum value */ + unsigned int efuse_min_value; + /* pre-defined efuse value for temperature calibration */ + unsigned int efuse_value; + /* pre-defined efuse range maximum value */ + unsigned int efuse_max_value; + /* current temperature sensing slope */ + unsigned int slope; +}; + +/* TMU device specific details and status */ +struct tmu_info { + /* base Address for the TMU */ + unsigned tmu_base; + /* pre-defined values for calibration and thresholds */ + struct tmu_data data; + /* value required for triminfo_25 calibration */ + unsigned int te1; + /* value required for triminfo_85 calibration */ + unsigned int te2; + /* Value for measured data calibration */ + int dc_value; + /* enum value indicating status of the TMU */ + int tmu_state; +}; + +/* Global struct tmu_info variable to store init values */ +static struct tmu_info gbl_info; + +/* + * Get current temperature code from register, + * then calculate and calibrate it's value + * in degree celsius. + * + * @return current temperature of the chip as sensed by TMU + */ +static int get_cur_temp(struct tmu_info *info) +{ + int cur_temp; + struct exynos5_tmu_reg *reg = (struct exynos5_tmu_reg *)info->tmu_base; + + /* + * Temperature code range between min 25 and max 125. + * May run more than once for first call as initial sensing + * has not yet happened. + */ + do { + cur_temp = readl(®->current_temp) & 0xff; + } while (cur_temp == 0 && info->tmu_state == TMU_STATUS_NORMAL); + + /* Calibrate current temperature */ + cur_temp = cur_temp - info->te1 + info->dc_value; + + return cur_temp; +} + +/* + * Monitors status of the TMU device and exynos temperature + * + * @param temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + */ +enum tmu_status_t tmu_monitor(int *temp) +{ + int cur_temp; + struct tmu_data *data = &gbl_info.data; + + if (gbl_info.tmu_state == TMU_STATUS_INIT) + return TMU_STATUS_INIT; + + /* Read current temperature of the SOC */ + cur_temp = get_cur_temp(&gbl_info); + *temp = cur_temp; + + /* Temperature code lies between min 25 and max 125 */ + if (cur_temp >= data->ts.start_tripping && + cur_temp <= data->ts.max_val) { + return TMU_STATUS_TRIPPED; + } else if (cur_temp >= data->ts.start_warning) { + return TMU_STATUS_WARNING; + } else if (cur_temp < data->ts.start_warning && + cur_temp >= data->ts.min_val) { + return TMU_STATUS_NORMAL; + } else { + /* Temperature code does not lie between min 25 and max 125 */ + gbl_info.tmu_state = TMU_STATUS_INIT; + debug("EXYNOS_TMU: Thermal reading failed\n"); + return TMU_STATUS_INIT; + } +} + +/* + * Get TMU specific pre-defined values from FDT + * + * @param info pointer to the tmu_info struct + * @param blob FDT blob + * @return int value, 0 for success + */ +static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) +{ +#ifdef CONFIG_OF_CONTROL + int node; + int error = 0; + + /* Get the node from FDT for TMU */ + node = fdtdec_next_compatible(blob, 0, + COMPAT_SAMSUNG_EXYNOS_TMU); + if (node < 0) { + debug("EXYNOS_TMU: No node for tmu in device tree\n"); + return -1; + } + + /* + * Get the pre-defined TMU specific values from FDT. + * All of these are expected to be correct otherwise + * miscalculation of register values in tmu_setup_parameters + * may result in misleading current temperature. + */ + info->tmu_base = fdtdec_get_addr(blob, node, "reg"); + if (info->tmu_base == FDT_ADDR_T_NONE) { + debug("%s: Missing tmu-base\n", __func__); + return -1; + } + info->data.ts.min_val = fdtdec_get_int(blob, + node, "samsung,min-temp", -1); + error |= info->data.ts.min_val; + info->data.ts.max_val = fdtdec_get_int(blob, + node, "samsung,max-temp", -1); + error |= info->data.ts.max_val; + info->data.ts.start_warning = fdtdec_get_int(blob, + node, "samsung,start-warning", -1); + error |= info->data.ts.start_warning; + info->data.ts.start_tripping = fdtdec_get_int(blob, + node, "samsung,start-tripping", -1); + error |= info->data.ts.start_tripping; + info->data.ts.hardware_tripping = fdtdec_get_int(blob, + node, "samsung,hw-tripping", -1); + error |= info->data.ts.hardware_tripping; + info->data.efuse_min_value = fdtdec_get_int(blob, + node, "samsung,efuse-min-value", -1); + error |= info->data.efuse_min_value; + info->data.efuse_value = fdtdec_get_int(blob, + node, "samsung,efuse-value", -1); + error |= info->data.efuse_value; + info->data.efuse_max_value = fdtdec_get_int(blob, + node, "samsung,efuse-max-value", -1); + error |= info->data.efuse_max_value; + info->data.slope = fdtdec_get_int(blob, + node, "samsung,slope", -1); + error |= info->data.slope; + info->dc_value = fdtdec_get_int(blob, + node, "samsung,dc-value", -1); + error |= info->dc_value; + + if (error == -1) { + debug("fail to get tmu node properties\n"); + return -1; + } +#endif + + return 0; +} + +/* + * Calibrate and calculate threshold values and + * enable interrupt levels + * + * @param info pointer to the tmu_info struct + */ +static void tmu_setup_parameters(struct tmu_info *info) +{ + unsigned int te_code, con; + unsigned int warning_code, trip_code, hwtrip_code; + unsigned int cooling_temp; + unsigned int rising_value; + struct tmu_data *data = &info->data; + struct exynos5_tmu_reg *reg = (struct exynos5_tmu_reg *)info->tmu_base; + + /* Must reload for reading efuse value from triminfo register */ + writel(TRIMINFO_RELOAD, ®->triminfo_control); + + /* Get the compensation parameter */ + te_code = readl(®->triminfo); + info->te1 = te_code & TRIM_INFO_MASK; + info->te2 = ((te_code >> 8) & TRIM_INFO_MASK); + + if ((data->efuse_min_value > info->te1) || + (info->te1 > data->efuse_max_value) + || (info->te2 != 0)) + info->te1 = data->efuse_value; + + /* Get RISING & FALLING Threshold value */ + warning_code = data->ts.start_warning + + info->te1 - info->dc_value; + trip_code = data->ts.start_tripping + + info->te1 - info->dc_value; + hwtrip_code = data->ts.hardware_tripping + + info->te1 - info->dc_value; + + cooling_temp = 0; + + rising_value = ((warning_code << 8) | + (trip_code << 16) | + (hwtrip_code << 24)); + + /* Set interrupt level */ + writel(rising_value, ®->threshold_temp_rise); + writel(cooling_temp, ®->threshold_temp_fall); + + /* + * Init TMU control tuning parameters + * [28:24] VREF - Voltage reference + * [15:13] THERM_TRIP_MODE - Tripping mode + * [12] THERM_TRIP_EN - Thermal tripping enable + * [11:8] BUF_SLOPE_SEL - Gain of amplifier + * [6] THERM_TRIP_BY_TQ_EN - Tripping by TQ pin + */ + writel(data->slope, ®->tmu_control); + + writel(INTCLEARALL, ®->intclear); + + /* TMU core enable */ + con = readl(®->tmu_control); + con |= THERM_TRIP_EN | CORE_EN; + + writel(con, ®->tmu_control); + + /* Enable HW thermal trip */ + set_hw_thermal_trip(); + + /* LEV1 LEV2 interrupt enable */ + writel(INTEN_RISE1 | INTEN_RISE2, ®->inten); +} + +/* + * Initialize TMU device + * + * @param blob FDT blob + * @return int value, 0 for success + */ +int tmu_init(const void *blob) +{ + gbl_info.tmu_state = TMU_STATUS_INIT; + if (get_tmu_fdt_values(&gbl_info, blob) < 0) + goto ret; + + tmu_setup_parameters(&gbl_info); + gbl_info.tmu_state = TMU_STATUS_NORMAL; +ret: + + return gbl_info.tmu_state; +} diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index b2da8b3..ed4e6b3 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -43,7 +43,7 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); #if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ - defined(CONFIG_AM33XX) + defined(CONFIG_AM33XX) || defined(CONFIG_TI814X) serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ #endif serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); @@ -57,7 +57,8 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); serial_out(UART_LCRVAL, &com_port->lcr); #if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ - defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) + defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ + defined(CONFIG_TI814X) #if defined(CONFIG_APTIX) /* /13 mode so Aptix 6MHz can hit 115200 */ diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 8fdffb1..1987ca1 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libsound.o COBJS-$(CONFIG_SOUND) += sound.o COBJS-$(CONFIG_I2S) += samsung-i2s.o COBJS-$(CONFIG_SOUND_WM8994) += wm8994.o +COBJS-$(CONFIG_SOUND_MAX98095) += max98095.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/sound/max98095.c b/drivers/sound/max98095.c new file mode 100644 index 0000000..d69db58 --- /dev/null +++ b/drivers/sound/max98095.c @@ -0,0 +1,550 @@ +/* + * max98095.c -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + * + * Modified for uboot by R. Chandrasekar (rcsekar@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/power.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <common.h> +#include <div64.h> +#include <fdtdec.h> +#include <i2c.h> +#include <sound.h> +#include "i2s.h" +#include "max98095.h" + +enum max98095_type { + MAX98095, +}; + +struct max98095_priv { + enum max98095_type devtype; + unsigned int sysclk; + unsigned int rate; + unsigned int fmt; +}; + +static struct sound_codec_info g_codec_info; +struct max98095_priv g_max98095_info; +unsigned int g_max98095_i2c_dev_addr; + +/* Index 0 is reserved. */ +int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000}; + +/* + * Writes value to a device register through i2c + * + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +static int max98095_i2c_write(unsigned int reg, unsigned char data) +{ + debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n", + __func__, reg, data); + return i2c_write(g_max98095_i2c_dev_addr, reg, 1, &data, 1); +} + +/* + * Read a value from a device register through i2c + * + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +static unsigned int max98095_i2c_read(unsigned int reg, unsigned char *data) +{ + int ret; + + ret = i2c_read(g_max98095_i2c_dev_addr, reg, 1, data, 1); + if (ret != 0) { + debug("%s: Error while reading register %#04x\n", + __func__, reg); + return -1; + } + + return 0; +} + +/* + * update device register bits through i2c + * + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 0 for success, non-zero error code. + */ +static int max98095_update_bits(unsigned int reg, unsigned char mask, + unsigned char value) +{ + int change, ret = 0; + unsigned char old, new; + + if (max98095_i2c_read(reg, &old) != 0) + return -1; + new = (old & ~mask) | (value & mask); + change = (old != new) ? 1 : 0; + if (change) + ret = max98095_i2c_write(reg, new); + if (ret < 0) + return ret; + + return change; +} + +/* + * codec mclk clock divider coefficients based on sampling rate + * + * @param rate sampling rate + * @param value address of indexvalue to be stored + * + * @return 0 for success or negative error code. + */ +static int rate_value(int rate, u8 *value) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= rate) { + *value = i; + return 0; + } + } + *value = 1; + + return -1; +} + +/* + * Sets hw params for max98095 + * + * @param max98095 max98095 information pointer + * @param rate Sampling rate + * @param bits_per_sample Bits per sample + * + * @return -1 for error and 0 Success. + */ +static int max98095_hw_params(struct max98095_priv *max98095, + unsigned int rate, unsigned int bits_per_sample) +{ + u8 regval; + int error; + + switch (bits_per_sample) { + case 16: + error = max98095_update_bits(M98095_034_DAI2_FORMAT, + M98095_DAI_WS, 0); + break; + case 24: + error = max98095_update_bits(M98095_034_DAI2_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + debug("%s: Illegal bits per sample %d.\n", + __func__, bits_per_sample); + return -1; + } + + if (rate_value(rate, ®val)) { + debug("%s: Failed to set sample rate to %d.\n", + __func__, rate); + return -1; + } + max98095->rate = rate; + + error |= max98095_update_bits(M98095_031_DAI2_CLKMODE, + M98095_CLKMODE_MASK, regval); + + /* Update sample rate mode */ + if (rate < 50000) + error |= max98095_update_bits(M98095_038_DAI2_FILTERS, + M98095_DAI_DHF, 0); + else + error |= max98095_update_bits(M98095_038_DAI2_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + if (error < 0) { + debug("%s: Error setting hardware params.\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface system clock for the given frequency + * + * @param max98095 max98095 information + * @param freq Sampling frequency in Hz + * + * @return -1 for error and 0 success. + */ +static int max98095_set_sysclk(struct max98095_priv *max98095, + unsigned int freq) +{ + int error = 0; + + /* Requested clock frequency is already setup */ + if (freq == max98095->sysclk) + return 0; + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + error = max98095_i2c_write(M98095_026_SYS_CLK, 0x10); + } else if ((freq >= 20000000) && (freq < 40000000)) { + error = max98095_i2c_write(M98095_026_SYS_CLK, 0x20); + } else if ((freq >= 40000000) && (freq < 60000000)) { + error = max98095_i2c_write(M98095_026_SYS_CLK, 0x30); + } else { + debug("%s: Invalid master clock frequency\n", __func__); + return -1; + } + + debug("%s: Clock at %uHz\n", __func__, freq); + + if (error < 0) + return -1; + + max98095->sysclk = freq; + return 0; +} + +/* + * Sets Max98095 I2S format + * + * @param max98095 max98095 information + * @param fmt i2S format - supports a subset of the options defined + * in i2s.h. + * + * @return -1 for error and 0 Success. + */ +static int max98095_set_fmt(struct max98095_priv *max98095, int fmt) +{ + u8 regval = 0; + int error = 0; + + if (fmt == max98095->fmt) + return 0; + + max98095->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Slave mode PLL */ + error |= max98095_i2c_write(M98095_032_DAI2_CLKCFG_HI, + 0x80); + error |= max98095_i2c_write(M98095_033_DAI2_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + regval |= M98095_DAI_MAS; + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + debug("%s: Clock mode unsupported\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + debug("%s: Unrecognized format.\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI | M98095_DAI_WCI; + break; + default: + debug("%s: Unrecognized inversion settings.\n", __func__); + return -1; + } + + error |= max98095_update_bits(M98095_034_DAI2_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + error |= max98095_i2c_write(M98095_035_DAI2_CLOCK, + M98095_DAI_BSEL64); + + if (error < 0) { + debug("%s: Error setting i2s format.\n", __func__); + return -1; + } + + return 0; +} + +/* + * resets the audio codec + * + * @return -1 for error and 0 success. + */ +static int max98095_reset(void) +{ + int i, ret; + + /* + * Gracefully reset the DSP core and the codec hardware in a proper + * sequence. + */ + ret = max98095_i2c_write(M98095_00F_HOST_CFG, 0); + if (ret != 0) { + debug("%s: Failed to reset DSP: %d\n", __func__, ret); + return ret; + } + + ret = max98095_i2c_write(M98095_097_PWR_SYS, 0); + if (ret != 0) { + debug("%s: Failed to reset codec: %d\n", __func__, ret); + return ret; + } + + /* + * Reset to hardware default for registers, as there is not a soft + * reset hardware control register. + */ + for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) { + ret = max98095_i2c_write(i, 0); + if (ret < 0) { + debug("%s: Failed to reset: %d\n", __func__, ret); + return ret; + } + } + + return 0; +} + +/* + * Intialise max98095 codec device + * + * @param max98095 max98095 information + * + * @returns -1 for error and 0 Success. + */ +static int max98095_device_init(struct max98095_priv *max98095) +{ + unsigned char id; + int error = 0; + + /* reset the codec, the DSP core, and disable all interrupts */ + error = max98095_reset(); + if (error != 0) { + debug("Reset\n"); + return error; + } + + /* initialize private data */ + max98095->sysclk = -1U; + max98095->rate = -1U; + max98095->fmt = -1U; + + error = max98095_i2c_read(M98095_0FF_REV_ID, &id); + if (error < 0) { + debug("%s: Failure reading hardware revision: %d\n", + __func__, id); + goto err_access; + } + debug("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A'); + + error |= max98095_i2c_write(M98095_097_PWR_SYS, M98095_PWRSV); + + /* + * initialize registers to hardware default configuring audio + * interface2 to DAC + */ + error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + M98095_DAI2M_TO_DACL|M98095_DAI2M_TO_DACR); + + error |= max98095_i2c_write(M98095_092_PWR_EN_OUT, + M98095_SPK_SPREADSPECTRUM); + error |= max98095_i2c_write(M98095_045_CFG_DSP, M98095_DSPNORMAL); + error |= max98095_i2c_write(M98095_04E_CFG_HP, M98095_HPNORMAL); + + error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, + M98095_S1NORMAL|M98095_SDATA); + + error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, + M98095_S2NORMAL|M98095_SDATA); + + error |= max98095_i2c_write(M98095_040_DAI3_IOCFG, + M98095_S3NORMAL|M98095_SDATA); + + /* take the codec out of the shut down */ + error |= max98095_update_bits(M98095_097_PWR_SYS, M98095_SHDNRUN, + M98095_SHDNRUN); + /* route DACL and DACR output to HO and Spekers */ + error |= max98095_i2c_write(M98095_050_MIX_SPK_LEFT, 0x01); /* DACL */ + error |= max98095_i2c_write(M98095_051_MIX_SPK_RIGHT, 0x01);/* DACR */ + error |= max98095_i2c_write(M98095_04C_MIX_HP_LEFT, 0x01); /* DACL */ + error |= max98095_i2c_write(M98095_04D_MIX_HP_RIGHT, 0x01); /* DACR */ + + /* power Enable */ + error |= max98095_i2c_write(M98095_091_PWR_EN_OUT, 0xF3); + + /* set Volume */ + error |= max98095_i2c_write(M98095_064_LVL_HP_L, 15); + error |= max98095_i2c_write(M98095_065_LVL_HP_R, 15); + error |= max98095_i2c_write(M98095_067_LVL_SPK_L, 16); + error |= max98095_i2c_write(M98095_068_LVL_SPK_R, 16); + + /* Enable DAIs */ + error |= max98095_i2c_write(M98095_093_BIAS_CTRL, 0x30); + error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07); + +err_access: + if (error < 0) + return -1; + + return 0; +} + +static int max98095_do_init(struct sound_codec_info *pcodec_info, + int sampling_rate, int mclk_freq, + int bits_per_sample) +{ + int ret = 0; + + /* Enable codec clock */ + set_xclkout(); + + /* shift the device address by 1 for 7 bit addressing */ + g_max98095_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; + + if (pcodec_info->codec_type == CODEC_MAX_98095) + g_max98095_info.devtype = MAX98095; + else { + debug("%s: Codec id [%d] not defined\n", __func__, + pcodec_info->codec_type); + return -1; + } + + ret = max98095_device_init(&g_max98095_info); + if (ret < 0) { + debug("%s: max98095 codec chip init failed\n", __func__); + return ret; + } + + ret = max98095_set_sysclk(&g_max98095_info, mclk_freq); + if (ret < 0) { + debug("%s: max98095 codec set sys clock failed\n", __func__); + return ret; + } + + ret = max98095_hw_params(&g_max98095_info, sampling_rate, + bits_per_sample); + + if (ret == 0) { + ret = max98095_set_fmt(&g_max98095_info, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + } + + return ret; +} + +static int get_max98095_codec_values(struct sound_codec_info *pcodec_info, + const void *blob) +{ + int error = 0; +#ifdef CONFIG_OF_CONTROL + enum fdt_compat_id compat; + int node; + int parent; + + /* Get the node from FDT for codec */ + node = fdtdec_next_compatible(blob, 0, COMPAT_MAXIM_98095_CODEC); + if (node <= 0) { + debug("EXYNOS_SOUND: No node for codec in device tree\n"); + debug("node = %d\n", node); + return -1; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + compat = fdtdec_lookup(blob, parent); + switch (compat) { + case COMPAT_SAMSUNG_S3C2440_I2C: + pcodec_info->i2c_bus = i2c_get_bus_num_fdt(parent); + error |= pcodec_info->i2c_bus; + debug("i2c bus = %d\n", pcodec_info->i2c_bus); + pcodec_info->i2c_dev_addr = fdtdec_get_int(blob, node, + "reg", 0); + error |= pcodec_info->i2c_dev_addr; + debug("i2c dev addr = %x\n", pcodec_info->i2c_dev_addr); + break; + default: + debug("%s: Unknown compat id %d\n", __func__, compat); + return -1; + } +#else + pcodec_info->i2c_bus = AUDIO_I2C_BUS; + pcodec_info->i2c_dev_addr = AUDIO_I2C_REG; + debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); +#endif + pcodec_info->codec_type = CODEC_MAX_98095; + if (error == -1) { + debug("fail to get max98095 codec node properties\n"); + return -1; + } + + return 0; +} + +/* max98095 Device Initialisation */ +int max98095_init(const void *blob, int sampling_rate, int mclk_freq, + int bits_per_sample) +{ + int ret; + int old_bus = i2c_get_bus_num(); + struct sound_codec_info *pcodec_info = &g_codec_info; + + if (get_max98095_codec_values(pcodec_info, blob) < 0) { + debug("FDT Codec values failed\n"); + return -1; + } + + i2c_set_bus_num(pcodec_info->i2c_bus); + ret = max98095_do_init(pcodec_info, sampling_rate, mclk_freq, + bits_per_sample); + i2c_set_bus_num(old_bus); + + return ret; +} diff --git a/drivers/sound/max98095.h b/drivers/sound/max98095.h new file mode 100644 index 0000000..ae5eb14 --- /dev/null +++ b/drivers/sound/max98095.h @@ -0,0 +1,311 @@ +/* + * max98095.h -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98095_H +#define _MAX98095_H + +/* + * MAX98095 Registers Definition + */ + +#define M98095_000_HOST_DATA 0x00 +#define M98095_001_HOST_INT_STS 0x01 +#define M98095_002_HOST_RSP_STS 0x02 +#define M98095_003_HOST_CMD_STS 0x03 +#define M98095_004_CODEC_STS 0x04 +#define M98095_005_DAI1_ALC_STS 0x05 +#define M98095_006_DAI2_ALC_STS 0x06 +#define M98095_007_JACK_AUTO_STS 0x07 +#define M98095_008_JACK_MANUAL_STS 0x08 +#define M98095_009_JACK_VBAT_STS 0x09 +#define M98095_00A_ACC_ADC_STS 0x0A +#define M98095_00B_MIC_NG_AGC_STS 0x0B +#define M98095_00C_SPK_L_VOLT_STS 0x0C +#define M98095_00D_SPK_R_VOLT_STS 0x0D +#define M98095_00E_TEMP_SENSOR_STS 0x0E +#define M98095_00F_HOST_CFG 0x0F +#define M98095_010_HOST_INT_CFG 0x10 +#define M98095_011_HOST_INT_EN 0x11 +#define M98095_012_CODEC_INT_EN 0x12 +#define M98095_013_JACK_INT_EN 0x13 +#define M98095_014_JACK_INT_EN 0x14 +#define M98095_015_DEC 0x15 +#define M98095_016_RESERVED 0x16 +#define M98095_017_RESERVED 0x17 +#define M98095_018_KEYCODE3 0x18 +#define M98095_019_KEYCODE2 0x19 +#define M98095_01A_KEYCODE1 0x1A +#define M98095_01B_KEYCODE0 0x1B +#define M98095_01C_OEMCODE1 0x1C +#define M98095_01D_OEMCODE0 0x1D +#define M98095_01E_XCFG1 0x1E +#define M98095_01F_XCFG2 0x1F +#define M98095_020_XCFG3 0x20 +#define M98095_021_XCFG4 0x21 +#define M98095_022_XCFG5 0x22 +#define M98095_023_XCFG6 0x23 +#define M98095_024_XGPIO 0x24 +#define M98095_025_XCLKCFG 0x25 +#define M98095_026_SYS_CLK 0x26 +#define M98095_027_DAI1_CLKMODE 0x27 +#define M98095_028_DAI1_CLKCFG_HI 0x28 +#define M98095_029_DAI1_CLKCFG_LO 0x29 +#define M98095_02A_DAI1_FORMAT 0x2A +#define M98095_02B_DAI1_CLOCK 0x2B +#define M98095_02C_DAI1_IOCFG 0x2C +#define M98095_02D_DAI1_TDM 0x2D +#define M98095_02E_DAI1_FILTERS 0x2E +#define M98095_02F_DAI1_LVL1 0x2F +#define M98095_030_DAI1_LVL2 0x30 +#define M98095_031_DAI2_CLKMODE 0x31 +#define M98095_032_DAI2_CLKCFG_HI 0x32 +#define M98095_033_DAI2_CLKCFG_LO 0x33 +#define M98095_034_DAI2_FORMAT 0x34 +#define M98095_035_DAI2_CLOCK 0x35 +#define M98095_036_DAI2_IOCFG 0x36 +#define M98095_037_DAI2_TDM 0x37 +#define M98095_038_DAI2_FILTERS 0x38 +#define M98095_039_DAI2_LVL1 0x39 +#define M98095_03A_DAI2_LVL2 0x3A +#define M98095_03B_DAI3_CLKMODE 0x3B +#define M98095_03C_DAI3_CLKCFG_HI 0x3C +#define M98095_03D_DAI3_CLKCFG_LO 0x3D +#define M98095_03E_DAI3_FORMAT 0x3E +#define M98095_03F_DAI3_CLOCK 0x3F +#define M98095_040_DAI3_IOCFG 0x40 +#define M98095_041_DAI3_TDM 0x41 +#define M98095_042_DAI3_FILTERS 0x42 +#define M98095_043_DAI3_LVL1 0x43 +#define M98095_044_DAI3_LVL2 0x44 +#define M98095_045_CFG_DSP 0x45 +#define M98095_046_DAC_CTRL1 0x46 +#define M98095_047_DAC_CTRL2 0x47 +#define M98095_048_MIX_DAC_LR 0x48 +#define M98095_049_MIX_DAC_M 0x49 +#define M98095_04A_MIX_ADC_LEFT 0x4A +#define M98095_04B_MIX_ADC_RIGHT 0x4B +#define M98095_04C_MIX_HP_LEFT 0x4C +#define M98095_04D_MIX_HP_RIGHT 0x4D +#define M98095_04E_CFG_HP 0x4E +#define M98095_04F_MIX_RCV 0x4F +#define M98095_050_MIX_SPK_LEFT 0x50 +#define M98095_051_MIX_SPK_RIGHT 0x51 +#define M98095_052_MIX_SPK_CFG 0x52 +#define M98095_053_MIX_LINEOUT1 0x53 +#define M98095_054_MIX_LINEOUT2 0x54 +#define M98095_055_MIX_LINEOUT_CFG 0x55 +#define M98095_056_LVL_SIDETONE_DAI12 0x56 +#define M98095_057_LVL_SIDETONE_DAI3 0x57 +#define M98095_058_LVL_DAI1_PLAY 0x58 +#define M98095_059_LVL_DAI1_EQ 0x59 +#define M98095_05A_LVL_DAI2_PLAY 0x5A +#define M98095_05B_LVL_DAI2_EQ 0x5B +#define M98095_05C_LVL_DAI3_PLAY 0x5C +#define M98095_05D_LVL_ADC_L 0x5D +#define M98095_05E_LVL_ADC_R 0x5E +#define M98095_05F_LVL_MIC1 0x5F +#define M98095_060_LVL_MIC2 0x60 +#define M98095_061_LVL_LINEIN 0x61 +#define M98095_062_LVL_LINEOUT1 0x62 +#define M98095_063_LVL_LINEOUT2 0x63 +#define M98095_064_LVL_HP_L 0x64 +#define M98095_065_LVL_HP_R 0x65 +#define M98095_066_LVL_RCV 0x66 +#define M98095_067_LVL_SPK_L 0x67 +#define M98095_068_LVL_SPK_R 0x68 +#define M98095_069_MICAGC_CFG 0x69 +#define M98095_06A_MICAGC_THRESH 0x6A +#define M98095_06B_SPK_NOISEGATE 0x6B +#define M98095_06C_DAI1_ALC1_TIME 0x6C +#define M98095_06D_DAI1_ALC1_COMP 0x6D +#define M98095_06E_DAI1_ALC1_EXPN 0x6E +#define M98095_06F_DAI1_ALC1_GAIN 0x6F +#define M98095_070_DAI1_ALC2_TIME 0x70 +#define M98095_071_DAI1_ALC2_COMP 0x71 +#define M98095_072_DAI1_ALC2_EXPN 0x72 +#define M98095_073_DAI1_ALC2_GAIN 0x73 +#define M98095_074_DAI1_ALC3_TIME 0x74 +#define M98095_075_DAI1_ALC3_COMP 0x75 +#define M98095_076_DAI1_ALC3_EXPN 0x76 +#define M98095_077_DAI1_ALC3_GAIN 0x77 +#define M98095_078_DAI2_ALC1_TIME 0x78 +#define M98095_079_DAI2_ALC1_COMP 0x79 +#define M98095_07A_DAI2_ALC1_EXPN 0x7A +#define M98095_07B_DAI2_ALC1_GAIN 0x7B +#define M98095_07C_DAI2_ALC2_TIME 0x7C +#define M98095_07D_DAI2_ALC2_COMP 0x7D +#define M98095_07E_DAI2_ALC2_EXPN 0x7E +#define M98095_07F_DAI2_ALC2_GAIN 0x7F +#define M98095_080_DAI2_ALC3_TIME 0x80 +#define M98095_081_DAI2_ALC3_COMP 0x81 +#define M98095_082_DAI2_ALC3_EXPN 0x82 +#define M98095_083_DAI2_ALC3_GAIN 0x83 +#define M98095_084_HP_NOISE_GATE 0x84 +#define M98095_085_AUX_ADC 0x85 +#define M98095_086_CFG_LINE 0x86 +#define M98095_087_CFG_MIC 0x87 +#define M98095_088_CFG_LEVEL 0x88 +#define M98095_089_JACK_DET_AUTO 0x89 +#define M98095_08A_JACK_DET_MANUAL 0x8A +#define M98095_08B_JACK_KEYSCAN_DBC 0x8B +#define M98095_08C_JACK_KEYSCAN_DLY 0x8C +#define M98095_08D_JACK_KEY_THRESH 0x8D +#define M98095_08E_JACK_DC_SLEW 0x8E +#define M98095_08F_JACK_TEST_CFG 0x8F +#define M98095_090_PWR_EN_IN 0x90 +#define M98095_091_PWR_EN_OUT 0x91 +#define M98095_092_PWR_EN_OUT 0x92 +#define M98095_093_BIAS_CTRL 0x93 +#define M98095_094_PWR_DAC_21 0x94 +#define M98095_095_PWR_DAC_03 0x95 +#define M98095_096_PWR_DAC_CK 0x96 +#define M98095_097_PWR_SYS 0x97 + +#define M98095_0FF_REV_ID 0xFF + +#define M98095_REG_CNT (0xFF+1) +#define M98095_REG_MAX_CACHED 0X97 + +/* MAX98095 Registers Bit Fields */ + +/* M98095_00F_HOST_CFG */ +#define M98095_SEG (1<<0) +#define M98095_XTEN (1<<1) +#define M98095_MDLLEN (1<<2) + +/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */ +#define M98095_CLKMODE_MASK 0xFF + +/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */ +#define M98095_DAI_MAS (1<<7) +#define M98095_DAI_WCI (1<<6) +#define M98095_DAI_BCI (1<<5) +#define M98095_DAI_DLY (1<<4) +#define M98095_DAI_TDM (1<<2) +#define M98095_DAI_FSW (1<<1) +#define M98095_DAI_WS (1<<0) + +/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */ +#define M98095_DAI_BSEL64 (1<<0) +#define M98095_DAI_DOSR_DIV2 (0<<5) +#define M98095_DAI_DOSR_DIV4 (1<<5) + +/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */ +#define M98095_S1NORMAL (1<<6) +#define M98095_S2NORMAL (2<<6) +#define M98095_S3NORMAL (3<<6) +#define M98095_SDATA (3<<0) + +/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */ +#define M98095_DAI_DHF (1<<3) + +/* M98095_045_DSP_CFG */ +#define M98095_DSPNORMAL (5<<4) + +/* M98095_048_MIX_DAC_LR */ +#define M98095_DAI1L_TO_DACR (1<<7) +#define M98095_DAI1R_TO_DACR (1<<6) +#define M98095_DAI2M_TO_DACR (1<<5) +#define M98095_DAI1L_TO_DACL (1<<3) +#define M98095_DAI1R_TO_DACL (1<<2) +#define M98095_DAI2M_TO_DACL (1<<1) +#define M98095_DAI3M_TO_DACL (1<<0) + +/* M98095_049_MIX_DAC_M */ +#define M98095_DAI1L_TO_DACM (1<<3) +#define M98095_DAI1R_TO_DACM (1<<2) +#define M98095_DAI2M_TO_DACM (1<<1) +#define M98095_DAI3M_TO_DACM (1<<0) + +/* M98095_04E_MIX_HP_CFG */ +#define M98095_HPNORMAL (3<<4) + +/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */ +#define M98095_MICPRE_MASK (3<<5) +#define M98095_MICPRE_SHIFT 5 + +/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */ +#define M98095_HP_MUTE (1<<7) + +/* M98095_066_LVL_RCV */ +#define M98095_REC_MUTE (1<<7) + +/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */ +#define M98095_SP_MUTE (1<<7) + +/* M98095_087_CFG_MIC */ +#define M98095_MICSEL_MASK (3<<0) +#define M98095_DIGMIC_L (1<<2) +#define M98095_DIGMIC_R (1<<3) +#define M98095_DIGMIC2L (1<<4) +#define M98095_DIGMIC2R (1<<5) + +/* M98095_088_CFG_LEVEL */ +#define M98095_VSEN (1<<6) +#define M98095_ZDEN (1<<5) +#define M98095_BQ2EN (1<<3) +#define M98095_BQ1EN (1<<2) +#define M98095_EQ2EN (1<<1) +#define M98095_EQ1EN (1<<0) + +/* M98095_090_PWR_EN_IN */ +#define M98095_INEN (1<<7) +#define M98095_MB2EN (1<<3) +#define M98095_MB1EN (1<<2) +#define M98095_MBEN (3<<2) +#define M98095_ADREN (1<<1) +#define M98095_ADLEN (1<<0) + +/* M98095_091_PWR_EN_OUT */ +#define M98095_HPLEN (1<<7) +#define M98095_HPREN (1<<6) +#define M98095_SPLEN (1<<5) +#define M98095_SPREN (1<<4) +#define M98095_RECEN (1<<3) +#define M98095_DALEN (1<<1) +#define M98095_DAREN (1<<0) + +/* M98095_092_PWR_EN_OUT */ +#define M98095_SPK_FIXEDSPECTRUM (0<<4) +#define M98095_SPK_SPREADSPECTRUM (1<<4) + +/* M98095_097_PWR_SYS */ +#define M98095_SHDNRUN (1<<7) +#define M98095_PERFMODE (1<<3) +#define M98095_HPPLYBACK (1<<2) +#define M98095_PWRSV8K (1<<1) +#define M98095_PWRSV (1<<0) + +#define M98095_COEFS_PER_BAND 5 + +/* Equalizer filter coefficients */ +#define M98095_110_DAI1_EQ_BASE 0x10 +#define M98095_142_DAI2_EQ_BASE 0x42 + +/* Biquad filter coefficients */ +#define M98095_174_DAI1_BQ_BASE 0x74 +#define M98095_17E_DAI2_BQ_BASE 0x7E + +/* function prototype */ + +/* + * intialise max98095 sound codec device for the given configuration + * + * @param blob FDT node for codec values + * @param sampling_rate Sampling rate (Hz) + * @param mclk_freq MCLK Frequency (Hz) + * @param bits_per_sample bits per Sample (must be 16 or 24) + * + * @returns -1 for error and 0 Success. + */ +int max98095_init(const void *blob, int sampling_rate, int mclk_freq, + int bits_per_sample); + +#endif diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c index fa8432d..a4bf4ad 100644 --- a/drivers/sound/sound.c +++ b/drivers/sound/sound.c @@ -31,6 +31,7 @@ #include <sound.h> #include <asm/arch/sound.h> #include "wm8994.h" +#include "max98095.h" /* defines */ #define SOUND_400_HZ 400 @@ -149,11 +150,15 @@ static int codec_init(const void *blob, struct i2stx_info *pi2s_tx) pi2s_tx->samplingrate, (pi2s_tx->samplingrate * (pi2s_tx->rfs)), pi2s_tx->bitspersample, pi2s_tx->channels); + } else if (!strcmp(codectype, "max98095")) { + ret = max98095_init(blob, pi2s_tx->samplingrate, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->bitspersample); } else { - debug("%s: Unknown code type %s\n", __func__, - codectype); + debug("%s: Unknown codec type %s\n", __func__, codectype); return -1; } + if (ret) { debug("%s: Codec init failed\n", __func__); return -1; diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4268595..d08609e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -50,8 +50,10 @@ COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o -COBJS-$(CONFIG_TEGRA_SPI) += tegra_spi.o -COBJS-$(CONFIG_TEGRA_SLINK) += tegra_slink.o +COBJS-$(CONFIG_FDT_SPI) += fdt_spi.o +COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o +COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o +COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o COBJS := $(COBJS-y) diff --git a/drivers/spi/fdt_spi.c b/drivers/spi/fdt_spi.c new file mode 100644 index 0000000..58f139a --- /dev/null +++ b/drivers/spi/fdt_spi.c @@ -0,0 +1,186 @@ +/* + * Common fdt based SPI driver front end + * + * Copyright (c) 2013 NVIDIA Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra20/tegra20_sflash.h> +#include <asm/arch-tegra20/tegra20_slink.h> +#include <asm/arch-tegra114/tegra114_spi.h> +#include <spi.h> +#include <fdtdec.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct fdt_spi_driver { + int compat; + int max_ctrls; + int (*init)(int *node_list, int count); + int (*claim_bus)(struct spi_slave *slave); + int (*release_bus)(struct spi_slave *slave); + int (*cs_is_valid)(unsigned int bus, unsigned int cs); + struct spi_slave *(*setup_slave)(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode); + void (*free_slave)(struct spi_slave *slave); + void (*cs_activate)(struct spi_slave *slave); + void (*cs_deactivate)(struct spi_slave *slave); + int (*xfer)(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags); +}; + +static struct fdt_spi_driver fdt_spi_drivers[] = { +#ifdef CONFIG_TEGRA20_SFLASH + { + .compat = COMPAT_NVIDIA_TEGRA20_SFLASH, + .max_ctrls = 1, + .init = tegra20_spi_init, + .claim_bus = tegra20_spi_claim_bus, + .cs_is_valid = tegra20_spi_cs_is_valid, + .setup_slave = tegra20_spi_setup_slave, + .free_slave = tegra20_spi_free_slave, + .cs_activate = tegra20_spi_cs_activate, + .cs_deactivate = tegra20_spi_cs_deactivate, + .xfer = tegra20_spi_xfer, + }, +#endif +#ifdef CONFIG_TEGRA20_SLINK + { + .compat = COMPAT_NVIDIA_TEGRA20_SLINK, + .max_ctrls = CONFIG_TEGRA_SLINK_CTRLS, + .init = tegra30_spi_init, + .claim_bus = tegra30_spi_claim_bus, + .cs_is_valid = tegra30_spi_cs_is_valid, + .setup_slave = tegra30_spi_setup_slave, + .free_slave = tegra30_spi_free_slave, + .cs_activate = tegra30_spi_cs_activate, + .cs_deactivate = tegra30_spi_cs_deactivate, + .xfer = tegra30_spi_xfer, + }, +#endif +#ifdef CONFIG_TEGRA114_SPI + { + .compat = COMPAT_NVIDIA_TEGRA114_SPI, + .max_ctrls = CONFIG_TEGRA114_SPI_CTRLS, + .init = tegra114_spi_init, + .claim_bus = tegra114_spi_claim_bus, + .cs_is_valid = tegra114_spi_cs_is_valid, + .setup_slave = tegra114_spi_setup_slave, + .free_slave = tegra114_spi_free_slave, + .cs_activate = tegra114_spi_cs_activate, + .cs_deactivate = tegra114_spi_cs_deactivate, + .xfer = tegra114_spi_xfer, + }, +#endif +}; + +static struct fdt_spi_driver *driver; + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (!driver) + return 0; + else if (!driver->cs_is_valid) + return 1; + else + return driver->cs_is_valid(bus, cs); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + if (!driver || !driver->setup_slave) + return NULL; + + return driver->setup_slave(bus, cs, max_hz, mode); +} + +void spi_free_slave(struct spi_slave *slave) +{ + if (driver && driver->free_slave) + return driver->free_slave(slave); +} + +static int spi_init_driver(struct fdt_spi_driver *driver) +{ + int count; + int node_list[driver->max_ctrls]; + + count = fdtdec_find_aliases_for_id(gd->fdt_blob, "spi", + driver->compat, + node_list, + driver->max_ctrls); + return driver->init(node_list, count); +} + +void spi_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fdt_spi_drivers); i++) { + driver = &fdt_spi_drivers[i]; + if (!spi_init_driver(driver)) + break; + } + if (i == ARRAY_SIZE(fdt_spi_drivers)) + driver = NULL; +} + +int spi_claim_bus(struct spi_slave *slave) +{ + if (!driver) + return 1; + if (!driver->claim_bus) + return 0; + + return driver->claim_bus(slave); +} + +void spi_release_bus(struct spi_slave *slave) +{ + if (driver && driver->release_bus) + driver->release_bus(slave); +} + +void spi_cs_activate(struct spi_slave *slave) +{ + if (driver && driver->cs_activate) + driver->cs_activate(slave); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + if (driver && driver->cs_deactivate) + driver->cs_deactivate(slave); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + if (!driver || !driver->xfer) + return -1; + + return driver->xfer(slave, bitlen, data_out, data_in, flags); +} diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c new file mode 100644 index 0000000..b11a0a1 --- /dev/null +++ b/drivers/spi/tegra114_spi.c @@ -0,0 +1,405 @@ +/* + * NVIDIA Tegra SPI controller (T114 and later) + * + * Copyright (c) 2010-2013 NVIDIA Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra114/tegra114_spi.h> +#include <spi.h> +#include <fdtdec.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* COMMAND1 */ +#define SPI_CMD1_GO (1 << 31) +#define SPI_CMD1_M_S (1 << 30) +#define SPI_CMD1_MODE_MASK 0x3 +#define SPI_CMD1_MODE_SHIFT 28 +#define SPI_CMD1_CS_SEL_MASK 0x3 +#define SPI_CMD1_CS_SEL_SHIFT 26 +#define SPI_CMD1_CS_POL_INACTIVE3 (1 << 25) +#define SPI_CMD1_CS_POL_INACTIVE2 (1 << 24) +#define SPI_CMD1_CS_POL_INACTIVE1 (1 << 23) +#define SPI_CMD1_CS_POL_INACTIVE0 (1 << 22) +#define SPI_CMD1_CS_SW_HW (1 << 21) +#define SPI_CMD1_CS_SW_VAL (1 << 20) +#define SPI_CMD1_IDLE_SDA_MASK 0x3 +#define SPI_CMD1_IDLE_SDA_SHIFT 18 +#define SPI_CMD1_BIDIR (1 << 17) +#define SPI_CMD1_LSBI_FE (1 << 16) +#define SPI_CMD1_LSBY_FE (1 << 15) +#define SPI_CMD1_BOTH_EN_BIT (1 << 14) +#define SPI_CMD1_BOTH_EN_BYTE (1 << 13) +#define SPI_CMD1_RX_EN (1 << 12) +#define SPI_CMD1_TX_EN (1 << 11) +#define SPI_CMD1_PACKED (1 << 5) +#define SPI_CMD1_BIT_LEN_MASK 0x1F +#define SPI_CMD1_BIT_LEN_SHIFT 0 + +/* COMMAND2 */ +#define SPI_CMD2_TX_CLK_TAP_DELAY (1 << 6) +#define SPI_CMD2_TX_CLK_TAP_DELAY_MASK (0x3F << 6) +#define SPI_CMD2_RX_CLK_TAP_DELAY (1 << 0) +#define SPI_CMD2_RX_CLK_TAP_DELAY_MASK (0x3F << 0) + +/* TRANSFER STATUS */ +#define SPI_XFER_STS_RDY (1 << 30) + +/* FIFO STATUS */ +#define SPI_FIFO_STS_CS_INACTIVE (1 << 31) +#define SPI_FIFO_STS_FRAME_END (1 << 30) +#define SPI_FIFO_STS_RX_FIFO_FLUSH (1 << 15) +#define SPI_FIFO_STS_TX_FIFO_FLUSH (1 << 14) +#define SPI_FIFO_STS_ERR (1 << 8) +#define SPI_FIFO_STS_TX_FIFO_OVF (1 << 7) +#define SPI_FIFO_STS_TX_FIFO_UNR (1 << 6) +#define SPI_FIFO_STS_RX_FIFO_OVF (1 << 5) +#define SPI_FIFO_STS_RX_FIFO_UNR (1 << 4) +#define SPI_FIFO_STS_TX_FIFO_FULL (1 << 3) +#define SPI_FIFO_STS_TX_FIFO_EMPTY (1 << 2) +#define SPI_FIFO_STS_RX_FIFO_FULL (1 << 1) +#define SPI_FIFO_STS_RX_FIFO_EMPTY (1 << 0) + +#define SPI_TIMEOUT 1000 +#define TEGRA_SPI_MAX_FREQ 52000000 + +struct spi_regs { + u32 command1; /* 000:SPI_COMMAND1 register */ + u32 command2; /* 004:SPI_COMMAND2 register */ + u32 timing1; /* 008:SPI_CS_TIM1 register */ + u32 timing2; /* 00c:SPI_CS_TIM2 register */ + u32 xfer_status;/* 010:SPI_TRANS_STATUS register */ + u32 fifo_status;/* 014:SPI_FIFO_STATUS register */ + u32 tx_data; /* 018:SPI_TX_DATA register */ + u32 rx_data; /* 01c:SPI_RX_DATA register */ + u32 dma_ctl; /* 020:SPI_DMA_CTL register */ + u32 dma_blk; /* 024:SPI_DMA_BLK register */ + u32 rsvd[56]; /* 028-107 reserved */ + u32 tx_fifo; /* 108:SPI_FIFO1 register */ + u32 rsvd2[31]; /* 10c-187 reserved */ + u32 rx_fifo; /* 188:SPI_FIFO2 register */ + u32 spare_ctl; /* 18c:SPI_SPARE_CTRL register */ +}; + +struct tegra_spi_ctrl { + struct spi_regs *regs; + unsigned int freq; + unsigned int mode; + int periph_id; + int valid; +}; + +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +static struct tegra_spi_ctrl spi_ctrls[CONFIG_TEGRA114_SPI_CTRLS]; + +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) +{ + return container_of(slave, struct tegra_spi_slave, slave); +} + +int tegra114_spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + if (bus >= CONFIG_TEGRA114_SPI_CTRLS || cs > 3 || !spi_ctrls[bus].valid) + return 0; + else + return 1; +} + +struct spi_slave *tegra114_spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct tegra_spi_slave *spi; + + debug("%s: bus: %u, cs: %u, max_hz: %u, mode: %u\n", __func__, + bus, cs, max_hz, mode); + + if (!spi_cs_is_valid(bus, cs)) { + printf("SPI error: unsupported bus %d / chip select %d\n", + bus, cs); + return NULL; + } + + if (max_hz > TEGRA_SPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz. Max frequency" + " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ); + return NULL; + } + + spi = malloc(sizeof(struct tegra_spi_slave)); + if (!spi) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + spi->slave.bus = bus; + spi->slave.cs = cs; + spi->ctrl = &spi_ctrls[bus]; + if (!spi->ctrl) { + printf("SPI error: could not find controller for bus %d\n", + bus); + return NULL; + } + + if (max_hz < spi->ctrl->freq) { + debug("%s: limiting frequency from %u to %u\n", __func__, + spi->ctrl->freq, max_hz); + spi->ctrl->freq = max_hz; + } + spi->ctrl->mode = mode; + + return &spi->slave; +} + +void tegra114_spi_free_slave(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + free(spi); +} + +int tegra114_spi_init(int *node_list, int count) +{ + struct tegra_spi_ctrl *ctrl; + int i; + int node = 0; + int found = 0; + + for (i = 0; i < count; i++) { + ctrl = &spi_ctrls[i]; + node = node_list[i]; + + ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { + debug("%s: no spi register found\n", __func__); + continue; + } + ctrl->freq = fdtdec_get_int(gd->fdt_blob, node, + "spi-max-frequency", 0); + if (!ctrl->freq) { + debug("%s: no spi max frequency found\n", __func__); + continue; + } + + ctrl->periph_id = clock_decode_periph_id(gd->fdt_blob, node); + if (ctrl->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + continue; + } + ctrl->valid = 1; + found = 1; + + debug("%s: found controller at %p, freq = %u, periph_id = %d\n", + __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); + } + + return !found; +} + +int tegra114_spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(spi->ctrl->periph_id, CLOCK_ID_PERIPH, + spi->ctrl->freq); + + /* Clear stale status here */ + setbits_le32(®s->fifo_status, + SPI_FIFO_STS_ERR | + SPI_FIFO_STS_TX_FIFO_OVF | + SPI_FIFO_STS_TX_FIFO_UNR | + SPI_FIFO_STS_RX_FIFO_OVF | + SPI_FIFO_STS_RX_FIFO_UNR | + SPI_FIFO_STS_TX_FIFO_FULL | + SPI_FIFO_STS_TX_FIFO_EMPTY | + SPI_FIFO_STS_RX_FIFO_FULL | + SPI_FIFO_STS_RX_FIFO_EMPTY); + debug("%s: FIFO STATUS = %08x\n", __func__, readl(®s->fifo_status)); + + /* Set master mode and sw controlled CS */ + setbits_le32(®s->command1, SPI_CMD1_M_S | SPI_CMD1_CS_SW_HW | + (spi->ctrl->mode << SPI_CMD1_MODE_SHIFT)); + debug("%s: COMMAND1 = %08x\n", __func__, readl(®s->command1)); + + return 0; +} + +void tegra114_spi_cs_activate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + clrbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL); +} + +void tegra114_spi_cs_deactivate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + + setbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL); +} + +int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_regs *regs = spi->ctrl->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes; + int ret; + + debug("%s: slave %u:%u dout %p din %p bitlen %u\n", + __func__, slave->bus, slave->cs, dout, din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + /* clear all error status bits */ + reg = readl(®s->fifo_status); + writel(reg, ®s->fifo_status); + + /* clear ready bit */ + setbits_le32(®s->xfer_status, SPI_XFER_STS_RDY); + + clrsetbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL, + SPI_CMD1_RX_EN | SPI_CMD1_TX_EN | SPI_CMD1_LSBY_FE | + (slave->cs << SPI_CMD1_CS_SEL_SHIFT)); + + /* set xfer size to 1 block (32 bits) */ + writel(0, ®s->dma_blk); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + int is_read = 0; + int tm, i; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | dout[i]; + dout += bytes; + } + + num_bytes -= bytes; + + clrsetbits_le32(®s->command1, + SPI_CMD1_BIT_LEN_MASK << SPI_CMD1_BIT_LEN_SHIFT, + (bytes * 8 - 1) << SPI_CMD1_BIT_LEN_SHIFT); + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command1, SPI_CMD1_GO); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { + u32 fifo_status, xfer_status; + + fifo_status = readl(®s->fifo_status); + + /* We can exit when we've had both RX and TX activity */ + if (is_read && + (fifo_status & SPI_FIFO_STS_TX_FIFO_EMPTY)) + break; + + xfer_status = readl(®s->xfer_status); + if (!(xfer_status & SPI_XFER_STS_RDY)) + continue; + + if (fifo_status & SPI_FIFO_STS_ERR) { + debug("%s: got a fifo error: ", __func__); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_OVF) + debug("tx FIFO overflow "); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_UNR) + debug("tx FIFO underrun "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_OVF) + debug("rx FIFO overflow "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_UNR) + debug("rx FIFO underrun "); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_FULL) + debug("tx FIFO full "); + if (fifo_status & SPI_FIFO_STS_TX_FIFO_EMPTY) + debug("tx FIFO empty "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_FULL) + debug("rx FIFO full "); + if (fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY) + debug("rx FIFO empty "); + debug("\n"); + break; + } + + if (!(fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + is_read = 1; + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + din[i] = tmpdin & 0xff; + tmpdin >>= 8; + } + din += bytes; + } + } + } + + if (tm >= SPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->fifo_status), ®s->fifo_status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n", + __func__, tmpdin, readl(®s->fifo_status)); + + if (ret) { + printf("%s: timeout during SPI transfer, tm %d\n", + __func__, ret); + return -1; + } + + return 0; +} diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra20_sflash.c index 05027af..9322ce7 100644 --- a/drivers/spi/tegra_spi.c +++ b/drivers/spi/tegra20_sflash.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 NVIDIA Corporation + * Copyright (c) 2010-2013 NVIDIA Corporation * With help from the mpc8xxx SPI driver * With more help from omap3_spi SPI driver * @@ -28,34 +28,80 @@ #include <asm/gpio.h> #include <asm/arch/clock.h> #include <asm/arch/pinmux.h> -#include <asm/arch/uart-spi-switch.h> #include <asm/arch-tegra/clk_rst.h> -#include <asm/arch-tegra/tegra_spi.h> +#include <asm/arch-tegra20/tegra20_sflash.h> #include <spi.h> #include <fdtdec.h> DECLARE_GLOBAL_DATA_PTR; -#if defined(CONFIG_SPI_CORRUPTS_UART) - #define corrupt_delay() udelay(CONFIG_SPI_CORRUPTS_UART_DLY); -#else - #define corrupt_delay() -#endif +#define SPI_CMD_GO (1 << 30) +#define SPI_CMD_ACTIVE_SCLK_SHIFT 26 +#define SPI_CMD_ACTIVE_SCLK_MASK (3 << SPI_CMD_ACTIVE_SCLK_SHIFT) +#define SPI_CMD_CK_SDA (1 << 21) +#define SPI_CMD_ACTIVE_SDA_SHIFT 18 +#define SPI_CMD_ACTIVE_SDA_MASK (3 << SPI_CMD_ACTIVE_SDA_SHIFT) +#define SPI_CMD_CS_POL (1 << 16) +#define SPI_CMD_TXEN (1 << 15) +#define SPI_CMD_RXEN (1 << 14) +#define SPI_CMD_CS_VAL (1 << 13) +#define SPI_CMD_CS_SOFT (1 << 12) +#define SPI_CMD_CS_DELAY (1 << 9) +#define SPI_CMD_CS3_EN (1 << 8) +#define SPI_CMD_CS2_EN (1 << 7) +#define SPI_CMD_CS1_EN (1 << 6) +#define SPI_CMD_CS0_EN (1 << 5) +#define SPI_CMD_BIT_LENGTH (1 << 4) +#define SPI_CMD_BIT_LENGTH_MASK 0x0000001F + +#define SPI_STAT_BSY (1 << 31) +#define SPI_STAT_RDY (1 << 30) +#define SPI_STAT_RXF_FLUSH (1 << 29) +#define SPI_STAT_TXF_FLUSH (1 << 28) +#define SPI_STAT_RXF_UNR (1 << 27) +#define SPI_STAT_TXF_OVF (1 << 26) +#define SPI_STAT_RXF_EMPTY (1 << 25) +#define SPI_STAT_RXF_FULL (1 << 24) +#define SPI_STAT_TXF_EMPTY (1 << 23) +#define SPI_STAT_TXF_FULL (1 << 22) +#define SPI_STAT_SEL_TXRX_N (1 << 16) +#define SPI_STAT_CUR_BLKCNT (1 << 15) + +#define SPI_TIMEOUT 1000 +#define TEGRA_SPI_MAX_FREQ 52000000 + +struct spi_regs { + u32 command; /* SPI_COMMAND_0 register */ + u32 status; /* SPI_STATUS_0 register */ + u32 rx_cmp; /* SPI_RX_CMP_0 register */ + u32 dma_ctl; /* SPI_DMA_CTL_0 register */ + u32 tx_fifo; /* SPI_TX_FIFO_0 register */ + u32 rsvd[3]; /* offsets 0x14 to 0x1F reserved */ + u32 rx_fifo; /* SPI_RX_FIFO_0 register */ +}; -struct tegra_spi_slave { - struct spi_slave slave; - struct spi_tegra *regs; +struct tegra_spi_ctrl { + struct spi_regs *regs; unsigned int freq; unsigned int mode; int periph_id; + int valid; }; +struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; +}; + +/* tegra20 only supports one SFLASH controller */ +static struct tegra_spi_ctrl spi_ctrls[1]; + static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) { return container_of(slave, struct tegra_spi_slave, slave); } -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +int tegra20_spi_cs_is_valid(unsigned int bus, unsigned int cs) { /* Tegra20 SPI-Flash - only 1 device ('bus/cs') */ if (bus != 0 || cs != 0) @@ -64,8 +110,8 @@ int spi_cs_is_valid(unsigned int bus, unsigned int cs) return 1; } -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) +struct spi_slave *tegra20_spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) { struct tegra_spi_slave *spi; @@ -86,86 +132,95 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, printf("SPI error: malloc of SPI structure failed\n"); return NULL; } -#ifdef CONFIG_OF_CONTROL - int node = fdtdec_next_compatible(gd->fdt_blob, 0, - COMPAT_NVIDIA_TEGRA20_SFLASH); - if (node < 0) { - debug("%s: cannot locate sflash node\n", __func__); + spi->slave.bus = bus; + spi->slave.cs = cs; + spi->ctrl = &spi_ctrls[bus]; + if (!spi->ctrl) { + printf("SPI error: could not find controller for bus %d\n", + bus); return NULL; } - if (!fdtdec_get_is_enabled(gd->fdt_blob, node)) { - debug("%s: sflash is disabled\n", __func__); - return NULL; - } - spi->regs = (struct spi_tegra *)fdtdec_get_addr(gd->fdt_blob, - node, "reg"); - if ((fdt_addr_t)spi->regs == FDT_ADDR_T_NONE) { - debug("%s: no sflash register found\n", __func__); - return NULL; - } - spi->freq = fdtdec_get_int(gd->fdt_blob, node, "spi-max-frequency", 0); - if (!spi->freq) { - debug("%s: no sflash max frequency found\n", __func__); - return NULL; - } - spi->periph_id = clock_decode_periph_id(gd->fdt_blob, node); - if (spi->periph_id == PERIPH_ID_NONE) { - debug("%s: could not decode periph id\n", __func__); - return NULL; - } -#else - spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE; - spi->freq = TEGRA_SPI_MAX_FREQ; - spi->periph_id = PERIPH_ID_SPI1; -#endif - if (max_hz < spi->freq) { + + if (max_hz < spi->ctrl->freq) { debug("%s: limiting frequency from %u to %u\n", __func__, - spi->freq, max_hz); - spi->freq = max_hz; + spi->ctrl->freq, max_hz); + spi->ctrl->freq = max_hz; } - debug("%s: controller initialized at %p, freq = %u, periph_id = %d\n", - __func__, spi->regs, spi->freq, spi->periph_id); - spi->mode = mode; + spi->ctrl->mode = mode; return &spi->slave; } -void spi_free_slave(struct spi_slave *slave) +void tegra20_spi_free_slave(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); free(spi); } -void spi_init(void) +int tegra20_spi_init(int *node_list, int count) { - /* do nothing */ + struct tegra_spi_ctrl *ctrl; + int i; + int node = 0; + int found = 0; + + for (i = 0; i < count; i++) { + ctrl = &spi_ctrls[i]; + node = node_list[i]; + + ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { + debug("%s: no slink register found\n", __func__); + continue; + } + ctrl->freq = fdtdec_get_int(gd->fdt_blob, node, + "spi-max-frequency", 0); + if (!ctrl->freq) { + debug("%s: no slink max frequency found\n", __func__); + continue; + } + + ctrl->periph_id = clock_decode_periph_id(gd->fdt_blob, node); + if (ctrl->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id\n", __func__); + continue; + } + ctrl->valid = 1; + found = 1; + + debug("%s: found controller at %p, freq = %u, periph_id = %d\n", + __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); + } + return !found; } -int spi_claim_bus(struct spi_slave *slave) +int tegra20_spi_claim_bus(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_tegra *regs = spi->regs; + struct spi_regs *regs = spi->ctrl->regs; u32 reg; /* Change SPI clock to correct frequency, PLLP_OUT0 source */ - clock_start_periph_pll(spi->periph_id, CLOCK_ID_PERIPH, spi->freq); + clock_start_periph_pll(spi->ctrl->periph_id, CLOCK_ID_PERIPH, + spi->ctrl->freq); /* Clear stale status here */ reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \ SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF; writel(reg, ®s->status); - debug("spi_init: STATUS = %08x\n", readl(®s->status)); + debug("%s: STATUS = %08x\n", __func__, readl(®s->status)); /* * Use sw-controlled CS, so we can clock in data after ReadID, etc. */ - reg = (spi->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; - if (spi->mode & 2) + reg = (spi->ctrl->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; + if (spi->ctrl->mode & 2) reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT; clrsetbits_le32(®s->command, SPI_CMD_ACTIVE_SCLK_MASK | SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg); - debug("spi_init: COMMAND = %08x\n", readl(®s->command)); + debug("%s: COMMAND = %08x\n", __func__, readl(®s->command)); /* * SPI pins on Tegra20 are muxed - change pinmux later due to UART @@ -173,58 +228,34 @@ int spi_claim_bus(struct spi_slave *slave) */ pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); pinmux_tristate_disable(PINGRP_LSPI); + pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); -#ifndef CONFIG_SPI_UART_SWITCH - /* - * NOTE: - * Only set PinMux bits 3:2 to SPI here on boards that don't have the - * SPI UART switch or subsequent UART data won't go out! See - * spi_uart_switch(). - */ - /* TODO: pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); */ -#endif return 0; } -void spi_release_bus(struct spi_slave *slave) -{ - /* - * We can't release UART_DISABLE and set pinmux to UART4 here since - * some code (e,g, spi_flash_probe) uses printf() while the SPI - * bus is held. That is arguably bad, but it has the advantage of - * already being in the source tree. - */ -} - -void spi_cs_activate(struct spi_slave *slave) +void tegra20_spi_cs_activate(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - - pinmux_select_spi(); + struct spi_regs *regs = spi->ctrl->regs; /* CS is negated on Tegra, so drive a 1 to get a 0 */ - setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); - - corrupt_delay(); /* Let UART settle */ + setbits_le32(®s->command, SPI_CMD_CS_VAL); } -void spi_cs_deactivate(struct spi_slave *slave) +void tegra20_spi_cs_deactivate(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - - pinmux_select_uart(); + struct spi_regs *regs = spi->ctrl->regs; /* CS is negated on Tegra, so drive a 0 to get a 1 */ - clrbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); - - corrupt_delay(); /* Let SPI settle */ + clrbits_le32(®s->command, SPI_CMD_CS_VAL); } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, +int tegra20_spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, void *data_in, unsigned long flags) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_tegra *regs = spi->regs; + struct spi_regs *regs = spi->ctrl->regs; u32 reg, tmpdout, tmpdin = 0; const u8 *dout = data_out; u8 *din = data_in; diff --git a/drivers/spi/tegra_slink.c b/drivers/spi/tegra20_slink.c index 9da5877..664de6e 100644 --- a/drivers/spi/tegra_slink.c +++ b/drivers/spi/tegra20_slink.c @@ -27,14 +27,68 @@ #include <asm/gpio.h> #include <asm/arch/clock.h> #include <asm/arch-tegra/clk_rst.h> -#include <asm/arch-tegra/tegra_slink.h> +#include <asm/arch-tegra20/tegra20_slink.h> #include <spi.h> #include <fdtdec.h> DECLARE_GLOBAL_DATA_PTR; +/* COMMAND */ +#define SLINK_CMD_ENB (1 << 31) +#define SLINK_CMD_GO (1 << 30) +#define SLINK_CMD_M_S (1 << 28) +#define SLINK_CMD_CK_SDA (1 << 21) +#define SLINK_CMD_CS_POL (1 << 13) +#define SLINK_CMD_CS_VAL (1 << 12) +#define SLINK_CMD_CS_SOFT (1 << 11) +#define SLINK_CMD_BIT_LENGTH (1 << 4) +#define SLINK_CMD_BIT_LENGTH_MASK 0x0000001F +/* COMMAND2 */ +#define SLINK_CMD2_TXEN (1 << 30) +#define SLINK_CMD2_RXEN (1 << 31) +#define SLINK_CMD2_SS_EN (1 << 18) +#define SLINK_CMD2_SS_EN_SHIFT 18 +#define SLINK_CMD2_SS_EN_MASK 0x000C0000 +#define SLINK_CMD2_CS_ACTIVE_BETWEEN (1 << 17) +/* STATUS */ +#define SLINK_STAT_BSY (1 << 31) +#define SLINK_STAT_RDY (1 << 30) +#define SLINK_STAT_ERR (1 << 29) +#define SLINK_STAT_RXF_FLUSH (1 << 27) +#define SLINK_STAT_TXF_FLUSH (1 << 26) +#define SLINK_STAT_RXF_OVF (1 << 25) +#define SLINK_STAT_TXF_UNR (1 << 24) +#define SLINK_STAT_RXF_EMPTY (1 << 23) +#define SLINK_STAT_RXF_FULL (1 << 22) +#define SLINK_STAT_TXF_EMPTY (1 << 21) +#define SLINK_STAT_TXF_FULL (1 << 20) +#define SLINK_STAT_TXF_OVF (1 << 19) +#define SLINK_STAT_RXF_UNR (1 << 18) +#define SLINK_STAT_CUR_BLKCNT (1 << 15) +/* STATUS2 */ +#define SLINK_STAT2_RXF_FULL_CNT (1 << 16) +#define SLINK_STAT2_TXF_FULL_CNT (1 << 0) + +#define SPI_TIMEOUT 1000 +#define TEGRA_SPI_MAX_FREQ 52000000 + +struct spi_regs { + u32 command; /* SLINK_COMMAND_0 register */ + u32 command2; /* SLINK_COMMAND2_0 reg */ + u32 status; /* SLINK_STATUS_0 register */ + u32 reserved; /* Reserved offset 0C */ + u32 mas_data; /* SLINK_MAS_DATA_0 reg */ + u32 slav_data; /* SLINK_SLAVE_DATA_0 reg */ + u32 dma_ctl; /* SLINK_DMA_CTL_0 register */ + u32 status2; /* SLINK_STATUS2_0 reg */ + u32 rsvd[56]; /* 0x20 to 0xFF reserved */ + u32 tx_fifo; /* SLINK_TX_FIFO_0 reg off 100h */ + u32 rsvd2[31]; /* 0x104 to 0x17F reserved */ + u32 rx_fifo; /* SLINK_RX_FIFO_0 reg off 180h */ +}; + struct tegra_spi_ctrl { - struct slink_tegra *regs; + struct spi_regs *regs; unsigned int freq; unsigned int mode; int periph_id; @@ -53,7 +107,7 @@ static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) return container_of(slave, struct tegra_spi_slave, slave); } -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +int tegra30_spi_cs_is_valid(unsigned int bus, unsigned int cs) { if (bus >= CONFIG_TEGRA_SLINK_CTRLS || cs > 3 || !spi_ctrls[bus].valid) return 0; @@ -61,7 +115,7 @@ int spi_cs_is_valid(unsigned int bus, unsigned int cs) return 1; } -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, +struct spi_slave *tegra30_spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct tegra_spi_slave *spi; @@ -103,32 +157,26 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, return &spi->slave; } -void spi_free_slave(struct spi_slave *slave) +void tegra30_spi_free_slave(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); free(spi); } -void spi_init(void) +int tegra30_spi_init(int *node_list, int count) { struct tegra_spi_ctrl *ctrl; int i; -#ifdef CONFIG_OF_CONTROL int node = 0; - int count; - int node_list[CONFIG_TEGRA_SLINK_CTRLS]; + int found = 0; - count = fdtdec_find_aliases_for_id(gd->fdt_blob, "spi", - COMPAT_NVIDIA_TEGRA20_SLINK, - node_list, - CONFIG_TEGRA_SLINK_CTRLS); for (i = 0; i < count; i++) { ctrl = &spi_ctrls[i]; node = node_list[i]; - ctrl->regs = (struct slink_tegra *)fdtdec_get_addr(gd->fdt_blob, - node, "reg"); + ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { debug("%s: no slink register found\n", __func__); continue; @@ -146,44 +194,18 @@ void spi_init(void) continue; } ctrl->valid = 1; + found = 1; debug("%s: found controller at %p, freq = %u, periph_id = %d\n", __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); } -#else - for (i = 0; i < CONFIG_TEGRA_SLINK_CTRLS; i++) { - ctrl = &spi_ctrls[i]; - u32 base_regs[] = { - NV_PA_SLINK1_BASE, - NV_PA_SLINK2_BASE, - NV_PA_SLINK3_BASE, - NV_PA_SLINK4_BASE, - NV_PA_SLINK5_BASE, - NV_PA_SLINK6_BASE, - }; - int periph_ids[] = { - PERIPH_ID_SBC1, - PERIPH_ID_SBC2, - PERIPH_ID_SBC3, - PERIPH_ID_SBC4, - PERIPH_ID_SBC5, - PERIPH_ID_SBC6, - }; - ctrl->regs = (struct slink_tegra *)base_regs[i]; - ctrl->freq = TEGRA_SPI_MAX_FREQ; - ctrl->periph_id = periph_ids[i]; - ctrl->valid = 1; - - debug("%s: found controller at %p, freq = %u, periph_id = %d\n", - __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); - } -#endif + return !found; } -int spi_claim_bus(struct spi_slave *slave) +int tegra30_spi_claim_bus(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct slink_tegra *regs = spi->ctrl->regs; + struct spi_regs *regs = spi->ctrl->regs; u32 reg; /* Change SPI clock to correct frequency, PLLP_OUT0 source */ @@ -205,33 +227,29 @@ int spi_claim_bus(struct spi_slave *slave) return 0; } -void spi_release_bus(struct spi_slave *slave) -{ -} - -void spi_cs_activate(struct spi_slave *slave) +void tegra30_spi_cs_activate(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct slink_tegra *regs = spi->ctrl->regs; + struct spi_regs *regs = spi->ctrl->regs; /* CS is negated on Tegra, so drive a 1 to get a 0 */ setbits_le32(®s->command, SLINK_CMD_CS_VAL); } -void spi_cs_deactivate(struct spi_slave *slave) +void tegra30_spi_cs_deactivate(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct slink_tegra *regs = spi->ctrl->regs; + struct spi_regs *regs = spi->ctrl->regs; /* CS is negated on Tegra, so drive a 0 to get a 1 */ clrbits_le32(®s->command, SLINK_CMD_CS_VAL); } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, +int tegra30_spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, void *data_in, unsigned long flags) { struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct slink_tegra *regs = spi->ctrl->regs; + struct spi_regs *regs = spi->ctrl->regs; u32 reg, tmpdout, tmpdin = 0; const u8 *dout = data_out; u8 *din = data_in; diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 250aebd..53952ab 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -41,6 +41,7 @@ COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o COBJS-$(CONFIG_S6E63D6) += s6e63d6.o COBJS-$(CONFIG_LD9040) += ld9040.o COBJS-$(CONFIG_SED156X) += sed156x.o +COBJS-$(CONFIG_VIDEO_BCM2835) += bcm2835.o COBJS-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o COBJS-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o COBJS-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c new file mode 100644 index 0000000..1e9a84a --- /dev/null +++ b/drivers/video/bcm2835.c @@ -0,0 +1,127 @@ +/* + * (C) Copyright 2012 Stephen Warren + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <lcd.h> +#include <asm/arch/mbox.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Global variables that lcd.c expects to exist */ +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; +void *lcd_base; +void *lcd_console_address; +short console_col; +short console_row; +vidinfo_t panel_info; +char lcd_cursor_enabled; +ushort lcd_cursor_width; +ushort lcd_cursor_height; + +struct msg_query { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_physical_w_h physical_w_h; + u32 end_tag; +}; + +struct msg_setup { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_physical_w_h physical_w_h; + struct bcm2835_mbox_tag_virtual_w_h virtual_w_h; + struct bcm2835_mbox_tag_depth depth; + struct bcm2835_mbox_tag_pixel_order pixel_order; + struct bcm2835_mbox_tag_alpha_mode alpha_mode; + struct bcm2835_mbox_tag_virtual_offset virtual_offset; + struct bcm2835_mbox_tag_overscan overscan; + struct bcm2835_mbox_tag_allocate_buffer allocate_buffer; + u32 end_tag; +}; + +void lcd_ctrl_init(void *lcdbase) +{ + ALLOC_ALIGN_BUFFER(struct msg_query, msg_query, 1, 16); + ALLOC_ALIGN_BUFFER(struct msg_setup, msg_setup, 1, 16); + int ret; + u32 w, h; + + debug("bcm2835: Query resolution...\n"); + + BCM2835_MBOX_INIT_HDR(msg_query); + BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_query->physical_w_h, + GET_PHYSICAL_W_H); + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_query->hdr); + if (ret) { + printf("bcm2835: Could not query display resolution\n"); + /* FIXME: How to disable the LCD to prevent errors? hang()? */ + return; + } + + w = msg_query->physical_w_h.body.resp.width; + h = msg_query->physical_w_h.body.resp.height; + + debug("bcm2835: Setting up display for %d x %d\n", w, h); + + BCM2835_MBOX_INIT_HDR(msg_setup); + BCM2835_MBOX_INIT_TAG(&msg_setup->physical_w_h, SET_PHYSICAL_W_H); + msg_setup->physical_w_h.body.req.width = w; + msg_setup->physical_w_h.body.req.height = h; + BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_w_h, SET_VIRTUAL_W_H); + msg_setup->virtual_w_h.body.req.width = w; + msg_setup->virtual_w_h.body.req.height = h; + BCM2835_MBOX_INIT_TAG(&msg_setup->depth, SET_DEPTH); + msg_setup->depth.body.req.bpp = 16; + BCM2835_MBOX_INIT_TAG(&msg_setup->pixel_order, SET_PIXEL_ORDER); + msg_setup->pixel_order.body.req.order = BCM2835_MBOX_PIXEL_ORDER_BGR; + BCM2835_MBOX_INIT_TAG(&msg_setup->alpha_mode, SET_ALPHA_MODE); + msg_setup->alpha_mode.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; + BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_offset, SET_VIRTUAL_OFFSET); + msg_setup->virtual_offset.body.req.x = 0; + msg_setup->virtual_offset.body.req.y = 0; + BCM2835_MBOX_INIT_TAG(&msg_setup->overscan, SET_OVERSCAN); + msg_setup->overscan.body.req.top = 0; + msg_setup->overscan.body.req.bottom = 0; + msg_setup->overscan.body.req.left = 0; + msg_setup->overscan.body.req.right = 0; + BCM2835_MBOX_INIT_TAG(&msg_setup->allocate_buffer, ALLOCATE_BUFFER); + msg_setup->allocate_buffer.body.req.alignment = 0x100; + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_setup->hdr); + if (ret) { + printf("bcm2835: Could not configure display\n"); + /* FIXME: How to disable the LCD to prevent errors? hang()? */ + return; + } + + w = msg_setup->physical_w_h.body.resp.width; + h = msg_setup->physical_w_h.body.resp.height; + + debug("bcm2835: Final resolution is %d x %d\n", w, h); + + panel_info.vl_col = w; + panel_info.vl_row = h; + panel_info.vl_bpix = LCD_COLOR16; + + gd->fb_base = msg_setup->allocate_buffer.body.resp.fb_address; + lcd_base = (void *)gd->fb_base; +} + +void lcd_enable(void) +{ +} |