Merge branch 'master' of git://git.denx.de/u-boot-arm

This commit is contained in:
Tom Rini
2012-12-22 05:55:19 -07:00
163 changed files with 8453 additions and 1096 deletions

View File

@@ -42,14 +42,14 @@ static unsigned long gpio_ports[] = {
[1] = GPIO2_BASE_ADDR,
[2] = GPIO3_BASE_ADDR,
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
defined(CONFIG_MX53) || defined(CONFIG_MX6)
[3] = GPIO4_BASE_ADDR,
#endif
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6)
[4] = GPIO5_BASE_ADDR,
[5] = GPIO6_BASE_ADDR,
#endif
#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
#if defined(CONFIG_MX53) || defined(CONFIG_MX6)
[6] = GPIO7_BASE_ADDR,
#endif
};

View File

@@ -144,9 +144,11 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio)
{
int bank = gpio / GPIO_PER_BANK;
bank *= sizeof(struct s5p_gpio_bank);
int bank;
unsigned g = gpio - s5p_gpio_part_max(gpio);
bank = g / GPIO_PER_BANK;
bank *= sizeof(struct s5p_gpio_bank);
return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank);
}

View File

@@ -63,6 +63,7 @@ static struct keyb {
struct kbc_tegra *kbc; /* tegra keyboard controller */
unsigned char inited; /* 1 if keyboard has been inited */
unsigned char first_scan; /* 1 if this is our first key scan */
unsigned char created; /* 1 if driver has been created */
/*
* After init we must wait a short time before polling the keyboard.
@@ -306,6 +307,10 @@ static void tegra_kbc_open(void)
*/
static int init_tegra_keyboard(void)
{
/* check if already created */
if (config.created)
return 0;
#ifdef CONFIG_OF_CONTROL
int node;
@@ -349,6 +354,7 @@ static int init_tegra_keyboard(void)
config_kbc_gpio(config.kbc);
tegra_kbc_open();
config.created = 1;
debug("%s: Tegra keyboard ready\n", __func__);
return 0;
@@ -357,6 +363,8 @@ static int init_tegra_keyboard(void)
int drv_keyboard_init(void)
{
struct stdio_dev dev;
char *stdinname = getenv("stdin");
int error;
if (input_init(&config.input, 0)) {
debug("%s: Cannot set up input\n", __func__);
@@ -372,5 +380,13 @@ int drv_keyboard_init(void)
dev.start = init_tegra_keyboard;
/* Register the device. init_tegra_keyboard() will be called soon */
return input_stdio_register(&dev);
error = input_stdio_register(&dev);
if (error)
return error;
#ifdef CONFIG_CONSOLE_MUX
error = iomux_doenv(stdin, stdinname);
if (error)
return error;
#endif
return 0;
}

View File

@@ -565,10 +565,11 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
mmc->getcd = tegra_mmc_getcd;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
mmc->host_caps = 0;
if (bus_width == 8)
mmc->host_caps = MMC_MODE_8BIT;
else
mmc->host_caps = MMC_MODE_4BIT;
mmc->host_caps |= MMC_MODE_8BIT;
if (bus_width >= 4)
mmc->host_caps |= MMC_MODE_4BIT;
mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
/*

View File

@@ -28,6 +28,7 @@ LIB := $(obj)libpmic.o
COBJS-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
COBJS-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
COBJS-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o
COBJS-$(CONFIG_POWER_MAX77686) += pmic_max77686.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2012 Samsung Electronics
* Rajeshwari Shinde <rajeshwari.s@samsung.com>
*
* 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.
*
* 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 <power/pmic.h>
#include <power/max77686_pmic.h>
#include <errno.h>
int pmic_init(unsigned char bus)
{
static const char name[] = "MAX77686_PMIC";
struct pmic *p = pmic_alloc();
if (!p) {
printf("%s: POWER allocation error!\n", __func__);
return -ENOMEM;
}
puts("Board PMIC init\n");
p->name = name;
p->interface = PMIC_I2C;
p->number_of_regs = PMIC_NUM_OF_REGS;
p->hw.i2c.addr = MAX77686_I2C_ADDR;
p->hw.i2c.tx_num = 1;
p->bus = bus;
return 0;
}

View File

@@ -27,6 +27,12 @@
#include <fsl_pmic.h>
#include <errno.h>
#if defined(CONFIG_PMIC_FSL_MC13892)
#define FSL_PMIC_I2C_LENGTH 3
#elif defined(CONFIG_PMIC_FSL_MC34704)
#define FSL_PMIC_I2C_LENGTH 1
#endif
#if defined(CONFIG_POWER_SPI)
static u32 pmic_spi_prepare_tx(u32 reg, u32 *val, u32 write)
{
@@ -59,7 +65,7 @@ int pmic_init(unsigned char bus)
#elif defined(CONFIG_POWER_I2C)
p->interface = PMIC_I2C;
p->hw.i2c.addr = CONFIG_SYS_FSL_PMIC_I2C_ADDR;
p->hw.i2c.tx_num = 3;
p->hw.i2c.tx_num = FSL_PMIC_I2C_LENGTH;
p->bus = bus;
#else
#error "You must select CONFIG_POWER_SPI or CONFIG_PMIC_I2C"

View File

@@ -22,7 +22,8 @@
*/
#include <common.h>
#include <linux/compiler.h>
#include <serial.h>
#include <asm/arch/s3c6400.h>
DECLARE_GLOBAL_DATA_PTR;

48
drivers/sound/Makefile Normal file
View File

@@ -0,0 +1,48 @@
#
# Copyright (C) 2012 Samsung Electronics
# R. Chandrasekar <rcsekar@samsung.com>
#
# 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.
#
# 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 $(TOPDIR)/config.mk
LIB := $(obj)libsound.o
COBJS-$(CONFIG_SOUND) += sound.o
COBJS-$(CONFIG_I2S) += samsung-i2s.o
COBJS-$(CONFIG_SOUND_WM8994) += wm8994.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#

358
drivers/sound/samsung-i2s.c Normal file
View File

@@ -0,0 +1,358 @@
/*
* Copyright (C) 2012 Samsung Electronics
* R. Chandrasekar <rcsekar@samsung.com>
*
* 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.
*
* 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 <asm/arch/clk.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/i2s-regs.h>
#include <asm/io.h>
#include <common.h>
#include <sound.h>
#include <i2s.h>
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
#define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */
/*
* Sets the frame size for I2S LR clock
*
* @param i2s_reg i2s regiter address
* @param rfs Frame Size
*/
static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs)
{
unsigned int mod = readl(&i2s_reg->mod);
mod &= ~MOD_RCLK_MASK;
switch (rfs) {
case 768:
mod |= MOD_RCLK_768FS;
break;
case 512:
mod |= MOD_RCLK_512FS;
break;
case 384:
mod |= MOD_RCLK_384FS;
break;
default:
mod |= MOD_RCLK_256FS;
break;
}
writel(mod, &i2s_reg->mod);
}
/*
* Sets the i2s transfer control
*
* @param i2s_reg i2s regiter address
* @param on 1 enable tx , 0 disable tx transfer
*/
static void i2s_txctrl(struct i2s_reg *i2s_reg, int on)
{
unsigned int con = readl(&i2s_reg->con);
unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK;
if (on) {
con |= CON_ACTIVE;
con &= ~CON_TXCH_PAUSE;
} else {
con |= CON_TXCH_PAUSE;
con &= ~CON_ACTIVE;
}
writel(mod, &i2s_reg->mod);
writel(con, &i2s_reg->con);
}
/*
* set the bit clock frame size (in multiples of LRCLK)
*
* @param i2s_reg i2s regiter address
* @param bfs bit Frame Size
*/
static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs)
{
unsigned int mod = readl(&i2s_reg->mod);
mod &= ~MOD_BCLK_MASK;
switch (bfs) {
case 48:
mod |= MOD_BCLK_48FS;
break;
case 32:
mod |= MOD_BCLK_32FS;
break;
case 24:
mod |= MOD_BCLK_24FS;
break;
case 16:
mod |= MOD_BCLK_16FS;
break;
default:
return;
}
writel(mod, &i2s_reg->mod);
}
/*
* flushes the i2stx fifo
*
* @param i2s_reg i2s regiter address
* @param flush Tx fifo flush command (0x00 - do not flush
* 0x80 - flush tx fifo)
*/
void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush)
{
/* Flush the FIFO */
setbits_le32(&i2s_reg->fic, flush);
clrbits_le32(&i2s_reg->fic, flush);
}
/*
* Set System Clock direction
*
* @param i2s_reg i2s regiter address
* @param dir Clock direction
*
* @return int value 0 for success, -1 in case of error
*/
int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir)
{
unsigned int mod = readl(&i2s_reg->mod);
if (dir == SND_SOC_CLOCK_IN)
mod |= MOD_CDCLKCON;
else
mod &= ~MOD_CDCLKCON;
writel(mod, &i2s_reg->mod);
return 0;
}
/*
* Sets I2S Clcok format
*
* @param fmt i2s clock properties
* @param i2s_reg i2s regiter address
*
* @return int value 0 for success, -1 in case of error
*/
int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)
{
unsigned int mod = readl(&i2s_reg->mod);
unsigned int tmp = 0;
unsigned int ret = 0;
/* Format is priority */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
tmp |= MOD_LR_RLOW;
tmp |= MOD_SDF_MSB;
break;
case SND_SOC_DAIFMT_LEFT_J:
tmp |= MOD_LR_RLOW;
tmp |= MOD_SDF_LSB;
break;
case SND_SOC_DAIFMT_I2S:
tmp |= MOD_SDF_IIS;
break;
default:
debug("%s: Invalid format priority [0x%x]\n", __func__,
(fmt & SND_SOC_DAIFMT_FORMAT_MASK));
return -1;
}
/*
* INV flag is relative to the FORMAT flag - if set it simply
* flips the polarity specified by the Standard
*/
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
if (tmp & MOD_LR_RLOW)
tmp &= ~MOD_LR_RLOW;
else
tmp |= MOD_LR_RLOW;
break;
default:
debug("%s: Invalid clock ploarity input [0x%x]\n", __func__,
(fmt & SND_SOC_DAIFMT_INV_MASK));
return -1;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
tmp |= MOD_SLAVE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* Set default source clock in Master mode */
ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT);
if (ret != 0) {
debug("%s:set i2s clock direction failed\n", __func__);
return -1;
}
break;
default:
debug("%s: Invalid master selection [0x%x]\n", __func__,
(fmt & SND_SOC_DAIFMT_MASTER_MASK));
return -1;
}
mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE);
mod |= tmp;
writel(mod, &i2s_reg->mod);
return 0;
}
/*
* Sets the sample width in bits
*
* @param blc samplewidth (size of sample in bits)
* @param i2s_reg i2s regiter address
*
* @return int value 0 for success, -1 in case of error
*/
int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc)
{
unsigned int mod = readl(&i2s_reg->mod);
mod &= ~MOD_BLCP_MASK;
mod &= ~MOD_BLC_MASK;
switch (blc) {
case 8:
mod |= MOD_BLCP_8BIT;
mod |= MOD_BLC_8BIT;
break;
case 16:
mod |= MOD_BLCP_16BIT;
mod |= MOD_BLC_16BIT;
break;
case 24:
mod |= MOD_BLCP_24BIT;
mod |= MOD_BLC_24BIT;
break;
default:
debug("%s: Invalid sample size input [0x%x]\n",
__func__, blc);
return -1;
}
writel(mod, &i2s_reg->mod);
return 0;
}
int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data,
unsigned long data_size)
{
int i;
int start;
struct i2s_reg *i2s_reg =
(struct i2s_reg *)pi2s_tx->base_address;
if (data_size < FIFO_LENGTH) {
debug("%s : Invalid data size\n", __func__);
return -1; /* invalid pcm data size */
}
/* fill the tx buffer before stating the tx transmit */
for (i = 0; i < FIFO_LENGTH; i++)
writel(*data++, &i2s_reg->txd);
data_size -= FIFO_LENGTH;
i2s_txctrl(i2s_reg, I2S_TX_ON);
while (data_size > 0) {
start = get_timer(0);
if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) {
writel(*data++, &i2s_reg->txd);
data_size--;
} else {
if (get_timer(start) > TIMEOUT_I2S_TX) {
i2s_txctrl(i2s_reg, I2S_TX_OFF);
debug("%s: I2S Transfer Timeout\n", __func__);
return -1;
}
}
}
i2s_txctrl(i2s_reg, I2S_TX_OFF);
return 0;
}
int i2s_tx_init(struct i2stx_info *pi2s_tx)
{
int ret;
struct i2s_reg *i2s_reg =
(struct i2s_reg *)pi2s_tx->base_address;
/* Initialize GPIO for I2s */
exynos_pinmux_config(PERIPH_ID_I2S1, 0);
/* Set EPLL Clock */
ret = set_epll_clk(pi2s_tx->audio_pll_clk);
if (ret != 0) {
debug("%s: epll clock set rate falied\n", __func__);
return -1;
}
/* Select Clk Source for Audio1 */
set_i2s_clk_source();
/* Set Prescaler to get MCLK */
set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk,
(pi2s_tx->samplingrate * (pi2s_tx->rfs)));
/* Configure I2s format */
ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM));
if (ret == 0) {
i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs);
ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample);
if (ret != 0) {
debug("%s:set sample rate failed\n", __func__);
return -1;
}
i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs);
/* disable i2s transfer flag and flush the fifo */
i2s_txctrl(i2s_reg, I2S_TX_OFF);
i2s_fifo(i2s_reg, FIC_TXFLUSH);
} else {
debug("%s: failed\n", __func__);
}
return ret;
}

228
drivers/sound/sound.c Normal file
View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2012 Samsung Electronics
* R. Chandrasekar <rcsekar@samsung.com>
*
* 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.
*
* 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 <malloc.h>
#include <common.h>
#include <asm/io.h>
#include <i2c.h>
#include <i2s.h>
#include <sound.h>
#include "wm8994.h"
#include <asm/arch/sound.h>
/* defines */
#define SOUND_400_HZ 400
#define SOUND_BITS_IN_BYTE 8
static struct i2stx_info g_i2stx_pri;
static struct sound_codec_info g_codec_info;
/*
* get_sound_fdt_values gets fdt values for i2s parameters
*
* @param i2stx_info i2s transmitter transfer param structure
* @param blob FDT blob
*/
static void get_sound_i2s_values(struct i2stx_info *i2s)
{
i2s->base_address = samsung_get_base_i2s();
i2s->audio_pll_clk = I2S_PLL_CLK;
i2s->samplingrate = I2S_SAMPLING_RATE;
i2s->bitspersample = I2S_BITS_PER_SAMPLE;
i2s->channels = I2S_CHANNELS;
i2s->rfs = I2S_RFS;
i2s->bfs = I2S_BFS;
}
/*
* Gets fdt values for wm8994 config parameters
*
* @param pcodec_info codec information structure
* @param blob FDT blob
* @return int value, 0 for success
*/
static int get_sound_wm8994_values(struct sound_codec_info *pcodec_info)
{
int error = 0;
switch (AUDIO_COMPAT) {
case AUDIO_COMPAT_SPI:
debug("%s: Support not added for SPI interface\n", __func__);
return -1;
break;
case AUDIO_COMPAT_I2C:
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);
break;
default:
debug("%s: Unknown compat id %d\n", __func__, AUDIO_COMPAT);
return -1;
}
if (error == -1) {
debug("fail to get wm8994 codec node properties\n");
return -1;
}
return 0;
}
/*
* Gets fdt values for codec config parameters
*
* @param pcodec_info codec information structure
* @param blob FDT blob
* @return int value, 0 for success
*/
static int get_sound_codec_values(struct sound_codec_info *pcodec_info)
{
int error = 0;
const char *codectype;
codectype = AUDIO_CODEC;
if (!strcmp(codectype, "wm8994")) {
pcodec_info->codec_type = CODEC_WM_8994;
error = get_sound_wm8994_values(pcodec_info);
} else {
error = -1;
}
if (error == -1) {
debug("fail to get sound codec node properties\n");
return -1;
}
return 0;
}
int sound_init(void)
{
int ret;
struct i2stx_info *pi2s_tx = &g_i2stx_pri;
struct sound_codec_info *pcodec_info = &g_codec_info;
/* Get the I2S Values */
get_sound_i2s_values(pi2s_tx);
/* Get the codec Values */
if (get_sound_codec_values(pcodec_info) < 0)
return -1;
ret = i2s_tx_init(pi2s_tx);
if (ret) {
debug("%s: Failed to init i2c transmit: ret=%d\n", __func__,
ret);
return ret;
}
/* Check the codec type and initialise the same */
if (pcodec_info->codec_type == CODEC_WM_8994) {
ret = wm8994_init(pcodec_info, WM8994_AIF2,
pi2s_tx->samplingrate,
(pi2s_tx->samplingrate * (pi2s_tx->rfs)),
pi2s_tx->bitspersample, pi2s_tx->channels);
} else {
debug("%s: Unknown code type %d\n", __func__,
pcodec_info->codec_type);
return -1;
}
if (ret) {
debug("%s: Codec init failed\n", __func__);
return -1;
}
return ret;
}
/*
* Generates square wave sound data for 1 second
*
* @param data data buffer pointer
* @param size size of the buffer
* @param freq frequency of the wave
*/
static void sound_prepare_buffer(unsigned short *data, int size, uint32_t freq)
{
const int sample = 48000;
const unsigned short amplitude = 16000; /* between 1 and 32767 */
const int period = freq ? sample / freq : 0;
const int half = period / 2;
assert(freq);
/* Make sure we don't overflow our buffer */
if (size % 2)
size--;
while (size) {
int i;
for (i = 0; size && i < half; i++) {
size -= 2;
*data++ = amplitude;
*data++ = amplitude;
}
for (i = 0; size && i < period - half; i++) {
size -= 2;
*data++ = -amplitude;
*data++ = -amplitude;
}
}
}
int sound_play(uint32_t msec, uint32_t frequency)
{
unsigned int *data;
unsigned long data_size;
unsigned int ret = 0;
/*Buffer length computation */
data_size = g_i2stx_pri.samplingrate * g_i2stx_pri.channels;
data_size *= (g_i2stx_pri.bitspersample / SOUND_BITS_IN_BYTE);
data = malloc(data_size);
if (data == NULL) {
debug("%s: malloc failed\n", __func__);
return -1;
}
sound_prepare_buffer((unsigned short *)data,
data_size / sizeof(unsigned short), frequency);
while (msec >= 1000) {
ret = i2s_transfer_tx_data(&g_i2stx_pri, data,
(data_size / sizeof(int)));
msec -= 1000;
}
if (msec) {
unsigned long size =
(data_size * msec) / (sizeof(int) * 1000);
ret = i2s_transfer_tx_data(&g_i2stx_pri, data, size);
}
free(data);
return ret;
}

792
drivers/sound/wm8994.c Normal file
View File

@@ -0,0 +1,792 @@
/*
* Copyright (C) 2012 Samsung Electronics
* R. Chandrasekar <rcsekar@samsung.com>
*
* 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.
*
* 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 <asm/arch/clk.h>
#include <asm/arch/cpu.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <common.h>
#include <div64.h>
#include <i2c.h>
#include <i2s.h>
#include <sound.h>
#include "wm8994.h"
#include "wm8994_registers.h"
/* defines for wm8994 system clock selection */
#define SEL_MCLK1 0x00
#define SEL_MCLK2 0x08
#define SEL_FLL1 0x10
#define SEL_FLL2 0x18
/* fll config to configure fll */
struct wm8994_fll_config {
int src; /* Source */
int in; /* Input frequency in Hz */
int out; /* output frequency in Hz */
};
/* codec private data */
struct wm8994_priv {
enum wm8994_type type; /* codec type of wolfson */
int revision; /* Revision */
int sysclk[WM8994_MAX_AIF]; /* System clock frequency in Hz */
int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */
int aifclk[WM8994_MAX_AIF]; /* audio interface clock in Hz */
struct wm8994_fll_config fll[2]; /* fll config to configure fll */
};
/* wm 8994 supported sampling rate values */
static unsigned int src_rate[] = {
8000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 88200, 96000
};
/* op clock divisions */
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
/* lr clock frame size ratio */
static int fs_ratios[] = {
64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536
};
/* bit clock divisors */
static int bclk_divs[] = {
10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480,
640, 880, 960, 1280, 1760, 1920
};
static struct wm8994_priv g_wm8994_info;
static unsigned char g_wm8994_i2c_dev_addr;
/*
* Initialise I2C for wm 8994
*
* @param bus no i2c bus number in which wm8994 is connected
*/
static void wm8994_i2c_init(int bus_no)
{
i2c_set_bus_num(bus_no);
}
/*
* 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 wm8994_i2c_write(unsigned int reg, unsigned short data)
{
unsigned char val[2];
val[0] = (unsigned char)((data >> 8) & 0xff);
val[1] = (unsigned char)(data & 0xff);
debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data);
return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2);
}
/*
* 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 wm8994_i2c_read(unsigned int reg , unsigned short *data)
{
unsigned char val[2];
int ret;
ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2);
if (ret != 0) {
debug("%s: Error while reading register %#04x\n",
__func__, reg);
return -1;
}
*data = val[0];
*data <<= 8;
*data |= val[1];
return 0;
}
/*
* update device register bits through i2c
*
* @param reg codec register
* @param mask register mask
* @param value new value
*
* @return int value 1 if change in the register value,
* 0 for no change or negative error code.
*/
static int wm8994_update_bits(unsigned int reg, unsigned short mask,
unsigned short value)
{
int change , ret = 0;
unsigned short old, new;
if (wm8994_i2c_read(reg, &old) != 0)
return -1;
new = (old & ~mask) | (value & mask);
change = (old != new) ? 1 : 0;
if (change)
ret = wm8994_i2c_write(reg, new);
if (ret < 0)
return ret;
return change;
}
/*
* Sets i2s set format
*
* @param aif_id Interface ID
* @param fmt i2S format
*
* @return -1 for error and 0 Success.
*/
int wm8994_set_fmt(int aif_id, unsigned int fmt)
{
int ms_reg;
int aif_reg;
int ms = 0;
int aif = 0;
int aif_clk = 0;
int error = 0;
switch (aif_id) {
case 1:
ms_reg = WM8994_AIF1_MASTER_SLAVE;
aif_reg = WM8994_AIF1_CONTROL_1;
aif_clk = WM8994_AIF1_CLOCKING_1;
break;
case 2:
ms_reg = WM8994_AIF2_MASTER_SLAVE;
aif_reg = WM8994_AIF2_CONTROL_1;
aif_clk = WM8994_AIF2_CLOCKING_1;
break;
default:
debug("%s: Invalid audio interface selection\n", __func__);
return -1;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
case SND_SOC_DAIFMT_CBM_CFM:
ms = WM8994_AIF1_MSTR;
break;
default:
debug("%s: Invalid i2s master selection\n", __func__);
return -1;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
aif |= WM8994_AIF1_LRCLK_INV;
case SND_SOC_DAIFMT_DSP_A:
aif |= 0x18;
break;
case SND_SOC_DAIFMT_I2S:
aif |= 0x10;
break;
case SND_SOC_DAIFMT_RIGHT_J:
break;
case SND_SOC_DAIFMT_LEFT_J:
aif |= 0x8;
break;
default:
debug("%s: Invalid i2s format selection\n", __func__);
return -1;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
/* frame inversion not valid for DSP modes */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
aif |= WM8994_AIF1_BCLK_INV;
break;
default:
debug("%s: Invalid i2s frame inverse selection\n",
__func__);
return -1;
}
break;
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_LEFT_J:
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
break;
case SND_SOC_DAIFMT_IB_NF:
aif |= WM8994_AIF1_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF:
aif |= WM8994_AIF1_LRCLK_INV;
break;
default:
debug("%s: Invalid i2s clock polarity selection\n",
__func__);
return -1;
}
break;
default:
debug("%s: Invalid i2s format selection\n", __func__);
return -1;
}
error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV |
WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif);
error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms);
error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK,
WM8994_AIF1CLK_ENA);
if (error < 0) {
debug("%s: codec register access error\n", __func__);
return -1;
}
return 0;
}
/*
* Sets hw params FOR WM8994
*
* @param wm8994 wm8994 information pointer
* @param aif_id Audio interface ID
* @param sampling_rate Sampling rate
* @param bits_per_sample Bits per sample
* @param Channels Channels in the given audio input
*
* @return -1 for error and 0 Success.
*/
static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id,
unsigned int sampling_rate, unsigned int bits_per_sample,
unsigned int channels)
{
int aif1_reg;
int aif2_reg;
int bclk_reg;
int bclk = 0;
int rate_reg;
int aif1 = 0;
int aif2 = 0;
int rate_val = 0;
int id = aif_id - 1;
int i, cur_val, best_val, bclk_rate, best;
unsigned short reg_data;
int ret = 0;
switch (aif_id) {
case 1:
aif1_reg = WM8994_AIF1_CONTROL_1;
aif2_reg = WM8994_AIF1_CONTROL_2;
bclk_reg = WM8994_AIF1_BCLK;
rate_reg = WM8994_AIF1_RATE;
break;
case 2:
aif1_reg = WM8994_AIF2_CONTROL_1;
aif2_reg = WM8994_AIF2_CONTROL_2;
bclk_reg = WM8994_AIF2_BCLK;
rate_reg = WM8994_AIF2_RATE;
break;
default:
return -1;
}
bclk_rate = sampling_rate * 32;
switch (bits_per_sample) {
case 16:
bclk_rate *= 16;
break;
case 20:
bclk_rate *= 20;
aif1 |= 0x20;
break;
case 24:
bclk_rate *= 24;
aif1 |= 0x40;
break;
case 32:
bclk_rate *= 32;
aif1 |= 0x60;
break;
default:
return -1;
}
/* Try to find an appropriate sample rate; look for an exact match. */
for (i = 0; i < ARRAY_SIZE(src_rate); i++)
if (src_rate[i] == sampling_rate)
break;
if (i == ARRAY_SIZE(src_rate)) {
debug("%s: Could not get the best matching samplingrate\n",
__func__);
return -1;
}
rate_val |= i << WM8994_AIF1_SR_SHIFT;
/* AIFCLK/fs ratio; look for a close match in either direction */
best = 0;
best_val = abs((fs_ratios[0] * sampling_rate)
- wm8994->aifclk[id]);
for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) {
cur_val = abs((fs_ratios[i] * sampling_rate)
- wm8994->aifclk[id]);
if (cur_val >= best_val)
continue;
best = i;
best_val = cur_val;
}
rate_val |= best;
/*
* We may not get quite the right frequency if using
* approximate clocks so look for the closest match that is
* higher than the target (we need to ensure that there enough
* BCLKs to clock out the samples).
*/
best = 0;
for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate;
if (cur_val < 0) /* BCLK table is sorted */
break;
best = i;
}
if (i == ARRAY_SIZE(bclk_divs)) {
debug("%s: Could not get the best matching bclk division\n",
__func__);
return -1;
}
bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best];
bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
if (wm8994_i2c_read(aif1_reg, &reg_data) != 0) {
debug("%s: AIF1 register read Failed\n", __func__);
return -1;
}
if ((channels == 1) && ((reg_data & 0x18) == 0x18))
aif2 |= WM8994_AIF1_MONO;
if (wm8994->aifclk[id] == 0) {
debug("%s:Audio interface clock not set\n", __func__);
return -1;
}
ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1);
ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2);
ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK |
WM8994_AIF1CLK_RATE_MASK, rate_val);
debug("rate vale = %x , bclk val= %x\n", rate_val, bclk);
if (ret < 0) {
debug("%s: codec register access error\n", __func__);
return -1;
}
return 0;
}
/*
* Configures Audio interface Clock
*
* @param wm8994 wm8994 information pointer
* @param aif Audio Interface ID
*
* @return -1 for error and 0 Success.
*/
static int configure_aif_clock(struct wm8994_priv *wm8994, int aif)
{
int rate;
int reg1 = 0;
int offset;
int ret;
/* AIF(1/0) register adress offset calculated */
if (aif)
offset = 4;
else
offset = 0;
switch (wm8994->sysclk[aif]) {
case WM8994_SYSCLK_MCLK1:
reg1 |= SEL_MCLK1;
rate = wm8994->mclk[0];
break;
case WM8994_SYSCLK_MCLK2:
reg1 |= SEL_MCLK2;
rate = wm8994->mclk[1];
break;
case WM8994_SYSCLK_FLL1:
reg1 |= SEL_FLL1;
rate = wm8994->fll[0].out;
break;
case WM8994_SYSCLK_FLL2:
reg1 |= SEL_FLL2;
rate = wm8994->fll[1].out;
break;
default:
debug("%s: Invalid input clock selection [%d]\n",
__func__, wm8994->sysclk[aif]);
return -1;
}
/* if input clock frequenct is more than 135Mhz then divide */
if (rate >= WM8994_MAX_INPUT_CLK_FREQ) {
rate /= 2;
reg1 |= WM8994_AIF1CLK_DIV;
}
wm8994->aifclk[aif] = rate;
ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset,
WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
reg1);
ret |= wm8994_update_bits(WM8994_CLOCKING_1,
WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK |
WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC |
WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA);
if (ret < 0) {
debug("%s: codec register access error\n", __func__);
return -1;
}
return 0;
}
/*
* Configures Audio interface for the given frequency
*
* @param wm8994 wm8994 information
* @param aif_id Audio Interface
* @param clk_id Input Clock ID
* @param freq Sampling frequency in Hz
*
* @return -1 for error and 0 success.
*/
static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id,
int clk_id, unsigned int freq)
{
int i;
int ret = 0;
wm8994->sysclk[aif_id - 1] = clk_id;
switch (clk_id) {
case WM8994_SYSCLK_MCLK1:
wm8994->mclk[0] = freq;
if (aif_id == 2) {
ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 ,
WM8994_AIF2DAC_DIV_MASK , 0);
}
break;
case WM8994_SYSCLK_MCLK2:
/* TODO: Set GPIO AF */
wm8994->mclk[1] = freq;
break;
case WM8994_SYSCLK_FLL1:
case WM8994_SYSCLK_FLL2:
break;
case WM8994_SYSCLK_OPCLK:
/*
* Special case - a division (times 10) is given and
* no effect on main clocking.
*/
if (freq) {
for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
if (opclk_divs[i] == freq)
break;
if (i == ARRAY_SIZE(opclk_divs)) {
debug("%s frequency divisor not found\n",
__func__);
return -1;
}
ret = wm8994_update_bits(WM8994_CLOCKING_2,
WM8994_OPCLK_DIV_MASK, i);
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2,
WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
} else {
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2,
WM8994_OPCLK_ENA, 0);
}
default:
debug("%s Invalid input clock selection [%d]\n",
__func__, clk_id);
return -1;
}
ret |= configure_aif_clock(wm8994, aif_id - 1);
if (ret < 0) {
debug("%s: codec register access error\n", __func__);
return -1;
}
return 0;
}
/*
* Initializes Volume for AIF2 to HP path
*
* @returns -1 for error and 0 Success.
*
*/
static int wm8994_init_volume_aif2_dac1(void)
{
int ret;
/* Unmute AIF2DAC */
ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1,
WM8994_AIF2DAC_MUTE_MASK, 0);
ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME,
WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK,
WM8994_AIF2DAC_VU | 0xff);
ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME,
WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK,
WM8994_AIF2DAC_VU | 0xff);
ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME,
WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK |
WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0);
ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME,
WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK |
WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0);
/* Head Phone Volume */
ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D);
ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D);
if (ret < 0) {
debug("%s: codec register access error\n", __func__);
return -1;
}
return 0;
}
/*
* Intialise wm8994 codec device
*
* @param wm8994 wm8994 information
*
* @returns -1 for error and 0 Success.
*/
static int wm8994_device_init(struct wm8994_priv *wm8994)
{
const char *devname;
unsigned short reg_data;
int ret;
wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */
ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, &reg_data);
if (ret < 0) {
debug("Failed to read ID register\n");
goto err;
}
if (reg_data == WM8994_ID) {
devname = "WM8994";
debug("Device registered as type %d\n", wm8994->type);
wm8994->type = WM8994;
} else {
debug("Device is not a WM8994, ID is %x\n", ret);
ret = -1;
goto err;
}
ret = wm8994_i2c_read(WM8994_CHIP_REVISION, &reg_data);
if (ret < 0) {
debug("Failed to read revision register: %d\n", ret);
goto err;
}
wm8994->revision = reg_data;
debug("%s revision %c\n", devname, 'A' + wm8994->revision);
/* VMID Selection */
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,
WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3);
/* Charge Pump Enable */
ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK,
WM8994_CP_ENA);
/* Head Phone Power Enable */
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,
WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA);
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,
WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA);
/* Power enable for AIF2 and DAC1 */
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5,
WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK |
WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK,
WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA |
WM8994_DAC1R_ENA);
/* Head Phone Initialisation */
ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1,
WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK,
WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY);
ret |= wm8994_update_bits(WM8994_DC_SERVO_1,
WM8994_DCS_ENA_CHAN_0_MASK |
WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 |
WM8994_DCS_ENA_CHAN_1);
ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1,
WM8994_HPOUT1L_DLY_MASK |
WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK |
WM8994_HPOUT1R_OUTP_MASK |
WM8994_HPOUT1L_RMV_SHORT_MASK |
WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY |
WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP |
WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT |
WM8994_HPOUT1R_RMV_SHORT);
/* MIXER Config DAC1 to HP */
ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1,
WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L);
ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2,
WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R);
/* Routing AIF2 to DAC1 */
ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING,
WM8994_AIF2DACL_TO_DAC1L_MASK,
WM8994_AIF2DACL_TO_DAC1L);
ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING,
WM8994_AIF2DACR_TO_DAC1R_MASK,
WM8994_AIF2DACR_TO_DAC1R);
/* GPIO Settings for AIF2 */
/* B CLK */
ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK |
WM8994_GPIO_FUNCTION_MASK ,
WM8994_GPIO_DIR_OUTPUT |
WM8994_GPIO_FUNCTION_I2S_CLK);
/* LR CLK */
ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK |
WM8994_GPIO_FUNCTION_MASK,
WM8994_GPIO_DIR_OUTPUT |
WM8994_GPIO_FUNCTION_I2S_CLK);
/* DATA */
ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK |
WM8994_GPIO_FUNCTION_MASK,
WM8994_GPIO_DIR_OUTPUT |
WM8994_GPIO_FUNCTION_I2S_CLK);
ret |= wm8994_init_volume_aif2_dac1();
if (ret < 0)
goto err;
debug("%s: Codec chip init ok\n", __func__);
return 0;
err:
debug("%s: Codec chip init error\n", __func__);
return -1;
}
/*wm8994 Device Initialisation */
int wm8994_init(struct sound_codec_info *pcodec_info,
enum en_audio_interface aif_id,
int sampling_rate, int mclk_freq,
int bits_per_sample, unsigned int channels)
{
int ret = 0;
/* shift the device address by 1 for 7 bit addressing */
g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr;
wm8994_i2c_init(pcodec_info->i2c_bus);
if (pcodec_info->codec_type == CODEC_WM_8994)
g_wm8994_info.type = WM8994;
else {
debug("%s: Codec id [%d] not defined\n", __func__,
pcodec_info->codec_type);
return -1;
}
ret = wm8994_device_init(&g_wm8994_info);
if (ret < 0) {
debug("%s: wm8994 codec chip init failed\n", __func__);
return ret;
}
ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1,
mclk_freq);
if (ret < 0) {
debug("%s: wm8994 codec set sys clock failed\n", __func__);
return ret;
}
ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate,
bits_per_sample, channels);
if (ret == 0) {
ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
}
return ret;
}

87
drivers/sound/wm8994.h Normal file
View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2012 Samsung Electronics
* R. Chadrasekar <rcsekar@samsung.com>
*
* 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.
*
* 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
*/
#ifndef __WM8994_H__
#define __WM8994_H__
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
#define WM8994_SYSCLK_MCLK2 2
#define WM8994_SYSCLK_FLL1 3
#define WM8994_SYSCLK_FLL2 4
/* Avilable audi interface ports in wm8994 codec */
enum en_audio_interface {
WM8994_AIF1 = 1,
WM8994_AIF2,
WM8994_AIF3
};
/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
#define WM8994_SYSCLK_OPCLK 5
#define WM8994_FLL1 1
#define WM8994_FLL2 2
#define WM8994_FLL_SRC_MCLK1 1
#define WM8994_FLL_SRC_MCLK2 2
#define WM8994_FLL_SRC_LRCLK 3
#define WM8994_FLL_SRC_BCLK 4
/* maximum available digital interfac in the dac to configure */
#define WM8994_MAX_AIF 2
#define WM8994_MAX_INPUT_CLK_FREQ 13500000
#define WM8994_ID 0x8994
enum wm8994_vmid_mode {
WM8994_VMID_NORMAL,
WM8994_VMID_FORCE,
};
/* wm 8994 family devices */
enum wm8994_type {
WM8994 = 0,
WM8958 = 1,
WM1811 = 2,
};
/*
* intialise wm8994 sound codec device for the given configuration
*
* @param pcodec_info pointer value of the sound codec info structure
* parsed from device tree
* @param aif_id enum value of codec interface port in which
* soc i2s is connected
* @param sampling_rate Sampling rate ranges between from 8khz to 96khz
* @param mclk_freq Master clock frequency.
* @param bits_per_sample bits per Sample can be 16 or 24
* @param channels Number of channnels, maximum 2
*
* @returns -1 for error and 0 Success.
*/
int wm8994_init(struct sound_codec_info *pcodec_info,
enum en_audio_interface aif_id,
int sampling_rate, int mclk_freq,
int bits_per_sample, unsigned int channels);
#endif /*__WM8994_H__ */

View File

@@ -0,0 +1,299 @@
/*
* (C) Copyright 2012 Samsung Electronics
*
* 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.
*
*/
#ifndef __WM8994_REGISTERS_H__
#define __WM8994_REGISTERS_H__
/*
* Register values.
*/
#define WM8994_SOFTWARE_RESET 0x00
#define WM8994_POWER_MANAGEMENT_1 0x01
#define WM8994_POWER_MANAGEMENT_2 0x02
#define WM8994_POWER_MANAGEMENT_5 0x05
#define WM8994_LEFT_OUTPUT_VOLUME 0x1C
#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D
#define WM8994_OUTPUT_MIXER_1 0x2D
#define WM8994_OUTPUT_MIXER_2 0x2E
#define WM8994_CHARGE_PUMP_1 0x4C
#define WM8994_DC_SERVO_1 0x54
#define WM8994_ANALOGUE_HP_1 0x60
#define WM8994_CHIP_REVISION 0x100
#define WM8994_AIF1_CLOCKING_1 0x200
#define WM8994_AIF1_CLOCKING_2 0x201
#define WM8994_AIF2_CLOCKING_1 0x204
#define WM8994_CLOCKING_1 0x208
#define WM8994_CLOCKING_2 0x209
#define WM8994_AIF1_RATE 0x210
#define WM8994_AIF2_RATE 0x211
#define WM8994_RATE_STATUS 0x212
#define WM8994_AIF1_CONTROL_1 0x300
#define WM8994_AIF1_CONTROL_2 0x301
#define WM8994_AIF1_MASTER_SLAVE 0x302
#define WM8994_AIF1_BCLK 0x303
#define WM8994_AIF2_CONTROL_1 0x310
#define WM8994_AIF2_CONTROL_2 0x311
#define WM8994_AIF2_MASTER_SLAVE 0x312
#define WM8994_AIF2_BCLK 0x313
#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502
#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503
#define WM8994_AIF2_DAC_FILTERS_1 0x520
#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601
#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602
#define WM8994_DAC1_LEFT_VOLUME 0x610
#define WM8994_DAC1_RIGHT_VOLUME 0x611
#define WM8994_GPIO_3 0x702
#define WM8994_GPIO_4 0x703
#define WM8994_GPIO_5 0x704
/*
* Field Definitions.
*/
/*
* R0 (0x00) - Software Reset
*/
/* SW_RESET */
#define WM8994_SW_RESET 1
/*
* R1 (0x01) - Power Management (1)
*/
/* HPOUT1L_ENA */
#define WM8994_HPOUT1L_ENA 0x0200
/* HPOUT1L_ENA */
#define WM8994_HPOUT1L_ENA_MASK 0x0200
/* HPOUT1R_ENA */
#define WM8994_HPOUT1R_ENA 0x0100
/* HPOUT1R_ENA */
#define WM8994_HPOUT1R_ENA_MASK 0x0100
/* VMID_SEL - [2:1] */
#define WM8994_VMID_SEL_MASK 0x0006
/* BIAS_ENA */
#define WM8994_BIAS_ENA 0x0001
/* BIAS_ENA */
#define WM8994_BIAS_ENA_MASK 0x0001
/*
* R2 (0x02) - Power Management (2)
*/
/* OPCLK_ENA */
#define WM8994_OPCLK_ENA 0x0800
/*
* R5 (0x05) - Power Management (5)
*/
/* AIF2DACL_ENA */
#define WM8994_AIF2DACL_ENA 0x2000
#define WM8994_AIF2DACL_ENA_MASK 0x2000
/* AIF2DACR_ENA */
#define WM8994_AIF2DACR_ENA 0x1000
#define WM8994_AIF2DACR_ENA_MASK 0x1000
/* DAC1L_ENA */
#define WM8994_DAC1L_ENA 0x0002
#define WM8994_DAC1L_ENA_MASK 0x0002
/* DAC1R_ENA */
#define WM8994_DAC1R_ENA 0x0001
#define WM8994_DAC1R_ENA_MASK 0x0001
/*
* R45 (0x2D) - Output Mixer (1)
*/
/* DAC1L_TO_HPOUT1L */
#define WM8994_DAC1L_TO_HPOUT1L 0x0100
#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100
/*
* R46 (0x2E) - Output Mixer (2)
*/
/* DAC1R_TO_HPOUT1R */
#define WM8994_DAC1R_TO_HPOUT1R 0x0100
#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100
/*
* R76 (0x4C) - Charge Pump (1)
*/
/* CP_ENA */
#define WM8994_CP_ENA 0x8000
#define WM8994_CP_ENA_MASK 0x8000
/*
* R84 (0x54) - DC Servo (1)
*/
/* DCS_ENA_CHAN_1 */
#define WM8994_DCS_ENA_CHAN_1 0x0002
#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002
/* DCS_ENA_CHAN_0 */
#define WM8994_DCS_ENA_CHAN_0 0x0001
#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001
/*
* R96 (0x60) - Analogue HP (1)
*/
/* HPOUT1L_RMV_SHORT */
#define WM8994_HPOUT1L_RMV_SHORT 0x0080
#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080
/* HPOUT1L_OUTP */
#define WM8994_HPOUT1L_OUTP 0x0040
#define WM8994_HPOUT1L_OUTP_MASK 0x0040
/* HPOUT1L_DLY */
#define WM8994_HPOUT1L_DLY 0x0020
#define WM8994_HPOUT1L_DLY_MASK 0x0020
/* HPOUT1R_RMV_SHORT */
#define WM8994_HPOUT1R_RMV_SHORT 0x0008
#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008
/* HPOUT1R_OUTP */
#define WM8994_HPOUT1R_OUTP 0x0004
#define WM8994_HPOUT1R_OUTP_MASK 0x0004
/* HPOUT1R_DLY */
#define WM8994_HPOUT1R_DLY 0x0002
#define WM8994_HPOUT1R_DLY_MASK 0x0002
/*
* R512 (0x200) - AIF1 Clocking (1)
*/
/* AIF1CLK_SRC - [4:3] */
#define WM8994_AIF1CLK_SRC_MASK 0x0018
/* AIF1CLK_DIV */
#define WM8994_AIF1CLK_DIV 0x0002
/* AIF1CLK_ENA */
#define WM8994_AIF1CLK_ENA 0x0001
#define WM8994_AIF1CLK_ENA_MASK 0x0001
/*
* R517 (0x205) - AIF2 Clocking (2)
*/
/* AIF2DAC_DIV - [5:3] */
#define WM8994_AIF2DAC_DIV_MASK 0x0038
/*
* R520 (0x208) - Clocking (1)
*/
/* AIF2DSPCLK_ENA */
#define WM8994_AIF2DSPCLK_ENA 0x0004
#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004
/* SYSDSPCLK_ENA */
#define WM8994_SYSDSPCLK_ENA 0x0002
#define WM8994_SYSDSPCLK_ENA_MASK 0x0002
/* SYSCLK_SRC */
#define WM8994_SYSCLK_SRC 0x0001
/*
* R521 (0x209) - Clocking (2)
*/
/* OPCLK_DIV - [2:0] */
#define WM8994_OPCLK_DIV_MASK 0x0007
/*
* R528 (0x210) - AIF1 Rate
*/
/* AIF1_SR - [7:4] */
#define WM8994_AIF1_SR_MASK 0x00F0
#define WM8994_AIF1_SR_SHIFT 4
/* AIF1CLK_RATE - [3:0] */
#define WM8994_AIF1CLK_RATE_MASK 0x000F
/*
* R768 (0x300) - AIF1 Control (1)
*/
/* AIF1_BCLK_INV */
#define WM8994_AIF1_BCLK_INV 0x0100
/* AIF1_LRCLK_INV */
#define WM8994_AIF1_LRCLK_INV 0x0080
#define WM8994_AIF1_LRCLK_INV_MASK 0x0080
/* AIF1_WL - [6:5] */
#define WM8994_AIF1_WL_MASK 0x0060
/* AIF1_FMT - [4:3] */
#define WM8994_AIF1_FMT_MASK 0x0018
/*
* R769 (0x301) - AIF1 Control (2)
*/
/* AIF1_MONO */
#define WM8994_AIF1_MONO 0x0100
/*
* R770 (0x302) - AIF1 Master/Slave
*/
/* AIF1_MSTR */
#define WM8994_AIF1_MSTR 0x4000
#define WM8994_AIF1_MSTR_MASK 0x4000
/*
* R771 (0x303) - AIF1 BCLK
*/
/* AIF1_BCLK_DIV - [8:4] */
#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0
#define WM8994_AIF1_BCLK_DIV_SHIFT 4
/*
* R1282 (0x502) - AIF2 DAC Left Volume
*/
/* AIF2DAC_VU */
#define WM8994_AIF2DAC_VU 0x0100
#define WM8994_AIF2DAC_VU_MASK 0x0100
/* AIF2DACL_VOL - [7:0] */
#define WM8994_AIF2DACL_VOL_MASK 0x00FF
/*
* R1283 (0x503) - AIF2 DAC Right Volume
*/
/* AIF2DACR_VOL - [7:0] */
#define WM8994_AIF2DACR_VOL_MASK 0x00FF
/*
* R1312 (0x520) - AIF2 DAC Filters (1)
*/
/* AIF2DAC_MUTE */
#define WM8994_AIF2DAC_MUTE_MASK 0x0200
/*
* R1537 (0x601) - DAC1 Left Mixer Routing
*/
/* AIF2DACL_TO_DAC1L */
#define WM8994_AIF2DACL_TO_DAC1L 0x0004
#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004
/*
* R1538 (0x602) - DAC1 Right Mixer Routing
*/
/* AIF2DACR_TO_DAC1R */
#define WM8994_AIF2DACR_TO_DAC1R 0x0004
#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004
/*
* R1552 (0x610) - DAC1 Left Volume
*/
/* DAC1L_MUTE */
#define WM8994_DAC1L_MUTE_MASK 0x0200
/* DAC1_VU */
#define WM8994_DAC1_VU 0x0100
#define WM8994_DAC1_VU_MASK 0x0100
/* DAC1L_VOL - [7:0] */
#define WM8994_DAC1L_VOL_MASK 0x00FF
/*
* R1553 (0x611) - DAC1 Right Volume
*/
/* DAC1R_MUTE */
#define WM8994_DAC1R_MUTE_MASK 0x0200
/* DAC1R_VOL - [7:0] */
#define WM8994_DAC1R_VOL_MASK 0x00FF
/*
* GPIO
*/
/* OUTPUT PIN */
#define WM8994_GPIO_DIR_OUTPUT 0x8000
/* GPIO PIN MASK */
#define WM8994_GPIO_DIR_MASK 0xFFE0
/* I2S CLK */
#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0000
/* GPn FN */
#define WM8994_GPIO_FUNCTION_MASK 0x001F
#endif

View File

@@ -34,6 +34,7 @@ COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o
COBJS-$(CONFIG_CF_SPI) += cf_spi.o
COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o

367
drivers/spi/exynos_spi.c Normal file
View File

@@ -0,0 +1,367 @@
/*
* (C) Copyright 2012 SAMSUNG Electronics
* Padmavathi Venna <padma.v@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/arch/clk.h>
#include <asm/arch/clock.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/pinmux.h>
#include <asm/arch-exynos/spi.h>
#include <asm/io.h>
/* Information about each SPI controller */
struct spi_bus {
enum periph_id periph_id;
s32 frequency; /* Default clock frequency, -1 for none */
struct exynos_spi *regs;
int inited; /* 1 if this bus is ready for use */
};
/* A list of spi buses that we know about */
static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS];
struct exynos_spi_slave {
struct spi_slave slave;
struct exynos_spi *regs;
unsigned int freq; /* Default frequency */
unsigned int mode;
enum periph_id periph_id; /* Peripheral ID for this device */
unsigned int fifo_size;
};
static struct spi_bus *spi_get_bus(unsigned dev_index)
{
if (dev_index < EXYNOS5_SPI_NUM_CONTROLLERS)
return &spi_bus[dev_index];
debug("%s: invalid bus %d", __func__, dev_index);
return NULL;
}
static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave)
{
return container_of(slave, struct exynos_spi_slave, slave);
}
/**
* Setup the driver private data
*
* @param bus ID of the bus that the slave is attached to
* @param cs ID of the chip select connected to the slave
* @param max_hz Required spi frequency
* @param mode Required spi mode (clk polarity, clk phase and
* master or slave)
* @return new device or NULL
*/
struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct exynos_spi_slave *spi_slave;
struct spi_bus *bus;
if (!spi_cs_is_valid(busnum, cs)) {
debug("%s: Invalid bus/chip select %d, %d\n", __func__,
busnum, cs);
return NULL;
}
spi_slave = malloc(sizeof(*spi_slave));
if (!spi_slave) {
debug("%s: Could not allocate spi_slave\n", __func__);
return NULL;
}
bus = &spi_bus[busnum];
spi_slave->slave.bus = busnum;
spi_slave->slave.cs = cs;
spi_slave->regs = bus->regs;
spi_slave->mode = mode;
spi_slave->periph_id = bus->periph_id;
if (bus->periph_id == PERIPH_ID_SPI1 ||
bus->periph_id == PERIPH_ID_SPI2)
spi_slave->fifo_size = 64;
else
spi_slave->fifo_size = 256;
spi_slave->freq = bus->frequency;
if (max_hz)
spi_slave->freq = min(max_hz, spi_slave->freq);
return &spi_slave->slave;
}
/**
* Free spi controller
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
void spi_free_slave(struct spi_slave *slave)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
free(spi_slave);
}
/**
* Flush spi tx, rx fifos and reset the SPI controller
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_flush_fifo(struct spi_slave *slave)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
struct exynos_spi *regs = spi_slave->regs;
clrsetbits_le32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
setbits_le32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
}
/**
* Initialize the spi base registers, set the required clock frequency and
* initialize the gpios
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
* @return zero on success else a negative value
*/
int spi_claim_bus(struct spi_slave *slave)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
struct exynos_spi *regs = spi_slave->regs;
u32 reg = 0;
int ret;
ret = set_spi_clk(spi_slave->periph_id,
spi_slave->freq);
if (ret < 0) {
debug("%s: Failed to setup spi clock\n", __func__);
return ret;
}
exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE);
spi_flush_fifo(slave);
reg = readl(&regs->ch_cfg);
reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L);
if (spi_slave->mode & SPI_CPHA)
reg |= SPI_CH_CPHA_B;
if (spi_slave->mode & SPI_CPOL)
reg |= SPI_CH_CPOL_L;
writel(reg, &regs->ch_cfg);
writel(SPI_FB_DELAY_180, &regs->fb_clk);
return 0;
}
/**
* Reset the spi H/W and flush the tx and rx fifos
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
void spi_release_bus(struct spi_slave *slave)
{
spi_flush_fifo(slave);
}
static void spi_get_fifo_levels(struct exynos_spi *regs,
int *rx_lvl, int *tx_lvl)
{
uint32_t spi_sts = readl(&regs->spi_sts);
*rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
*tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
}
/**
* If there's something to transfer, do a software reset and set a
* transaction size.
*
* @param regs SPI peripheral registers
* @param count Number of bytes to transfer
*/
static void spi_request_bytes(struct exynos_spi *regs, int count)
{
assert(count && count < (1 << 16));
setbits_le32(&regs->ch_cfg, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
writel(count | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
}
static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
void **dinp, void const **doutp)
{
struct exynos_spi *regs = spi_slave->regs;
uchar *rxp = *dinp;
const uchar *txp = *doutp;
int rx_lvl, tx_lvl;
uint out_bytes, in_bytes;
out_bytes = in_bytes = todo;
/*
* If there's something to send, do a software reset and set a
* transaction size.
*/
spi_request_bytes(regs, todo);
/*
* Bytes are transmitted/received in pairs. Wait to receive all the
* data because then transmission will be done as well.
*/
while (in_bytes) {
int temp;
/* Keep the fifos full/empty. */
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
if (tx_lvl < spi_slave->fifo_size && out_bytes) {
temp = txp ? *txp++ : 0xff;
writel(temp, &regs->tx_data);
out_bytes--;
}
if (rx_lvl > 0 && in_bytes) {
temp = readl(&regs->rx_data);
if (rxp)
*rxp++ = temp;
in_bytes--;
}
}
*dinp = rxp;
*doutp = txp;
}
/**
* Transfer and receive data
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
* @param bitlen No of bits to tranfer or receive
* @param dout Pointer to transfer buffer
* @param din Pointer to receive buffer
* @param flags Flags for transfer begin and end
* @return zero on success else a negative value
*/
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
int upto, todo;
int bytelen;
/* spi core configured to do 8 bit transfers */
if (bitlen % 8) {
debug("Non byte aligned SPI transfer.\n");
return -1;
}
/* Start the transaction, if necessary. */
if ((flags & SPI_XFER_BEGIN))
spi_cs_activate(slave);
/* Exynos SPI limits each transfer to 65535 bytes */
bytelen = bitlen / 8;
for (upto = 0; upto < bytelen; upto += todo) {
todo = min(bytelen - upto, (1 << 16) - 1);
spi_rx_tx(spi_slave, todo, &din, &dout);
}
/* Stop the transaction, if necessary. */
if ((flags & SPI_XFER_END))
spi_cs_deactivate(slave);
return 0;
}
/**
* Validates the bus and chip select numbers
*
* @param bus ID of the bus that the slave is attached to
* @param cs ID of the chip select connected to the slave
* @return one on success else zero
*/
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return spi_get_bus(bus) && cs == 0;
}
/**
* Activate the CS by driving it LOW
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
void spi_cs_activate(struct spi_slave *slave)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
debug("Activate CS, bus %d\n", spi_slave->slave.bus);
}
/**
* Deactivate the CS by driving it HIGH
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
void spi_cs_deactivate(struct spi_slave *slave)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
debug("Deactivate CS, bus %d\n", spi_slave->slave.bus);
}
static inline struct exynos_spi *get_spi_base(int dev_index)
{
if (dev_index < 3)
return (struct exynos_spi *)samsung_get_base_spi() + dev_index;
else
return (struct exynos_spi *)samsung_get_base_spi_isp() +
(dev_index - 3);
}
/* Sadly there is no error return from this function */
void spi_init(void)
{
int i;
struct spi_bus *bus;
for (i = 0; i < EXYNOS5_SPI_NUM_CONTROLLERS; i++) {
bus = &spi_bus[i];
bus->regs = get_spi_base(i);
bus->periph_id = PERIPH_ID_SPI0 + i;
/* Although Exynos5 supports upto 50Mhz speed,
* we are setting it to 10Mhz for safe side
*/
bus->frequency = 10000000;
bus->inited = 1;
}
}

View File

@@ -140,8 +140,8 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,
reg_ctrl = reg_read(&regs->ctrl);
/* Reset spi */
reg_write(&regs->ctrl, 0);
reg_write(&regs->ctrl, (reg_ctrl | 0x1));
reg_write(&regs->ctrl, (reg_ctrl & ~MXC_CSPICTRL_EN));
reg_write(&regs->ctrl, (reg_ctrl | MXC_CSPICTRL_EN));
/*
* The following computation is taken directly from Freescale's code.
@@ -387,7 +387,7 @@ static int decode_cs(struct mxc_spi_slave *mxcs, unsigned int cs)
if (cs > 3) {
mxcs->gpio = cs >> 8;
cs &= 3;
ret = gpio_direction_output(mxcs->gpio, 0);
ret = gpio_direction_output(mxcs->gpio, !(mxcs->ss_pol));
if (ret) {
printf("mxc_spi: cannot setup gpio %d\n", mxcs->gpio);
return -EINVAL;
@@ -414,6 +414,8 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
return NULL;
}
mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0;
ret = decode_cs(mxcs, cs);
if (ret < 0) {
free(mxcs);
@@ -425,7 +427,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
mxcs->slave.bus = bus;
mxcs->slave.cs = cs;
mxcs->base = spi_bases[bus];
mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0;
ret = spi_cfg_mxc(mxcs, cs, max_hz, mode);
if (ret) {

View File

@@ -29,34 +29,63 @@
#define MX5_USBOTHER_REGS_OFFSET 0x800
#define MXC_OTG_OFFSET 0
#define MXC_H1_OFFSET 0x200
#define MXC_H2_OFFSET 0x400
#define MXC_OTG_OFFSET 0
#define MXC_H1_OFFSET 0x200
#define MXC_H2_OFFSET 0x400
#define MXC_H3_OFFSET 0x600
#define MXC_USBCTRL_OFFSET 0
#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8
#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc
#define MXC_USB_CTRL_1_OFFSET 0x10
#define MXC_USBH2CTRL_OFFSET 0x14
#define MXC_USBH3CTRL_OFFSET 0x18
/* USB_CTRL */
#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */
#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */
#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */
#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */
#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */
/* OTG wakeup intr enable */
#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27)
/* OTG power mask */
#define MXC_OTG_UCTRL_OPM_BIT (1 << 24)
/* OTG power pin polarity */
#define MXC_OTG_UCTRL_O_PWR_POL_BIT (1 << 24)
/* Host1 ULPI interrupt enable */
#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12)
/* HOST1 wakeup intr enable */
#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11)
/* HOST1 power mask */
#define MXC_H1_UCTRL_H1PM_BIT (1 << 8)
/* HOST1 power pin polarity */
#define MXC_H1_UCTRL_H1_PWR_POL_BIT (1 << 8)
/* USB_PHY_CTRL_FUNC */
#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */
#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */
/* OTG Polarity of Overcurrent */
#define MXC_OTG_PHYCTRL_OC_POL_BIT (1 << 9)
/* OTG Disable Overcurrent Event */
#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8)
/* UH1 Polarity of Overcurrent */
#define MXC_H1_OC_POL_BIT (1 << 6)
/* UH1 Disable Overcurrent Event */
#define MXC_H1_OC_DIS_BIT (1 << 5)
/* OTG Power Pin Polarity */
#define MXC_OTG_PHYCTRL_PWR_POL_BIT (1 << 3)
/* USBH2CTRL */
#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8)
#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7)
#define MXC_H2_UCTRL_H2PM_BIT (1 << 4)
#define MXC_H2_UCTRL_H2_OC_POL_BIT (1 << 31)
#define MXC_H2_UCTRL_H2_OC_DIS_BIT (1 << 30)
#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8)
#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7)
#define MXC_H2_UCTRL_H2PM_BIT (1 << 4)
#define MXC_H2_UCTRL_H2_PWR_POL_BIT (1 << 4)
/* USBH3CTRL */
#define MXC_H3_UCTRL_H3_OC_POL_BIT (1 << 31)
#define MXC_H3_UCTRL_H3_OC_DIS_BIT (1 << 30)
#define MXC_H3_UCTRL_H3UIE_BIT (1 << 8)
#define MXC_H3_UCTRL_H3WIE_BIT (1 << 7)
#define MXC_H3_UCTRL_H3_PWR_POL_BIT (1 << 4)
/* USB_CTRL_1 */
#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
/* USB pin configuration */
#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \
@@ -143,24 +172,42 @@ int mxc_set_usbcontrol(int port, unsigned int flags)
if (flags & MXC_EHCI_INTERNAL_PHY) {
v = __raw_readl(usbother_base +
MXC_USB_PHY_CTR_FUNC_OFFSET);
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
/* OC/USBPWR is not used */
v |= MXC_OTG_PHYCTRL_OC_DIS_BIT;
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
v |= MXC_OTG_PHYCTRL_OC_POL_BIT;
else
v &= ~MXC_OTG_PHYCTRL_OC_POL_BIT;
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
/* OC/USBPWR is used */
v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT;
else
/* OC/USBPWR is not used */
v |= MXC_OTG_PHYCTRL_OC_DIS_BIT;
#ifdef CONFIG_MX51
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MXC_OTG_PHYCTRL_PWR_POL_BIT;
else
v &= ~MXC_OTG_PHYCTRL_PWR_POL_BIT;
#endif
__raw_writel(v, usbother_base +
MXC_USB_PHY_CTR_FUNC_OFFSET);
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
#ifdef CONFIG_MX51
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v |= MXC_OTG_UCTRL_OPM_BIT;
else
v &= ~MXC_OTG_UCTRL_OPM_BIT;
else
v |= MXC_OTG_UCTRL_OPM_BIT;
#endif
#ifdef CONFIG_MX53
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MXC_OTG_UCTRL_O_PWR_POL_BIT;
else
v &= ~MXC_OTG_UCTRL_O_PWR_POL_BIT;
#endif
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
}
break;
case 1: /* Host 1 Host ULPI */
case 1: /* Host 1 ULPI */
#ifdef CONFIG_MX51
/* The clock for the USBH1 ULPI port will come externally
from the PHY. */
@@ -170,13 +217,25 @@ int mxc_set_usbcontrol(int port, unsigned int flags)
#endif
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
#ifdef CONFIG_MX51
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */
v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */
else
v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */
v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */
#endif
#ifdef CONFIG_MX53
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MXC_H1_UCTRL_H1_PWR_POL_BIT;
else
v &= ~MXC_H1_UCTRL_H1_PWR_POL_BIT;
#endif
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET);
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
v |= MXC_H1_OC_POL_BIT;
else
v &= ~MXC_H1_OC_POL_BIT;
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */
else
@@ -186,24 +245,59 @@ int mxc_set_usbcontrol(int port, unsigned int flags)
break;
case 2: /* Host 2 ULPI */
v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET);
#ifdef CONFIG_MX51
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */
v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */
else
v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */
v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */
#endif
#ifdef CONFIG_MX53
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
v |= MXC_H2_UCTRL_H2_OC_POL_BIT;
else
v &= ~MXC_H2_UCTRL_H2_OC_POL_BIT;
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */
else
v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MXC_H2_UCTRL_H2_PWR_POL_BIT;
else
v &= ~MXC_H2_UCTRL_H2_PWR_POL_BIT;
#endif
__raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET);
break;
#ifdef CONFIG_MX53
case 3: /* Host 3 ULPI */
v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET);
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
v |= MXC_H3_UCTRL_H3_OC_POL_BIT;
else
v &= ~MXC_H3_UCTRL_H3_OC_POL_BIT;
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */
else
v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MXC_H3_UCTRL_H3_PWR_POL_BIT;
else
v &= ~MXC_H3_UCTRL_H3_PWR_POL_BIT;
__raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET);
break;
#endif
}
return ret;
}
void __board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
int __weak board_ehci_hcd_init(int port)
{
return 0;
}
void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
__attribute((weak, alias("__board_ehci_hcd_postinit")));
void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
{
}
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{

View File

@@ -159,6 +159,11 @@ static void usbh1_oc_config(void)
__raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
}
int __weak board_ehci_hcd_init(int port)
{
return 0;
}
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
struct usb_ehci *ehci;

View File

@@ -28,14 +28,22 @@
#define USBCTRL_OTGBASE_OFFSET 0x600
#ifdef CONFIG_MX25
#define MX25_USB_CTRL_IP_PUE_DOWN_BIT (1<<6)
#define MX25_USB_CTRL_HSTD_BIT (1<<5)
#define MX25_USB_CTRL_USBTE_BIT (1<<4)
#define MX25_USB_CTRL_OCPOL_OTG_BIT (1<<3)
#endif
#define MX25_OTG_SIC_SHIFT 29
#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
#define MX25_OTG_PM_BIT (1 << 24)
#define MX25_OTG_PP_BIT (1 << 11)
#define MX25_OTG_OCPOL_BIT (1 << 3)
#define MX25_H1_SIC_SHIFT 21
#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
#define MX25_H1_PP_BIT (1 << 18)
#define MX25_H1_PM_BIT (1 << 16)
#define MX25_H1_IPPUE_UP_BIT (1 << 7)
#define MX25_H1_IPPUE_DOWN_BIT (1 << 6)
#define MX25_H1_TLL_BIT (1 << 5)
#define MX25_H1_USBTE_BIT (1 << 4)
#define MX25_H1_OCPOL_BIT (1 << 2)
#ifdef CONFIG_MX31
#define MX31_OTG_SIC_SHIFT 29
#define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT)
#define MX31_OTG_PM_BIT (1 << 24)
@@ -49,59 +57,166 @@
#define MX31_H1_SIC_MASK (0x3 << MX31_H1_SIC_SHIFT)
#define MX31_H1_PM_BIT (1 << 8)
#define MX31_H1_DT_BIT (1 << 4)
#endif
#define MX35_OTG_SIC_SHIFT 29
#define MX35_OTG_SIC_MASK (0x3 << MX35_OTG_SIC_SHIFT)
#define MX35_OTG_PM_BIT (1 << 24)
#define MX35_OTG_PP_BIT (1 << 11)
#define MX35_OTG_OCPOL_BIT (1 << 3)
#define MX35_H1_SIC_SHIFT 21
#define MX35_H1_SIC_MASK (0x3 << MX35_H1_SIC_SHIFT)
#define MX35_H1_PP_BIT (1 << 18)
#define MX35_H1_PM_BIT (1 << 16)
#define MX35_H1_IPPUE_UP_BIT (1 << 7)
#define MX35_H1_IPPUE_DOWN_BIT (1 << 6)
#define MX35_H1_TLL_BIT (1 << 5)
#define MX35_H1_USBTE_BIT (1 << 4)
#define MX35_H1_OCPOL_BIT (1 << 2)
static int mxc_set_usbcontrol(int port, unsigned int flags)
{
unsigned int v;
#ifdef CONFIG_MX25
v = MX25_USB_CTRL_IP_PUE_DOWN_BIT | MX25_USB_CTRL_HSTD_BIT |
MX25_USB_CTRL_USBTE_BIT | MX25_USB_CTRL_OCPOL_OTG_BIT;
v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
#if defined(CONFIG_MX25)
switch (port) {
case 0: /* OTG port */
v &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PM_BIT | MX25_OTG_PP_BIT |
MX25_OTG_OCPOL_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX25_OTG_PM_BIT;
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MX25_OTG_PP_BIT;
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
v |= MX25_OTG_OCPOL_BIT;
break;
case 1: /* H1 port */
v &= ~(MX25_H1_SIC_MASK | MX25_H1_PM_BIT | MX25_H1_PP_BIT |
MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT |
MX25_H1_IPPUE_UP_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX25_H1_PM_BIT;
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MX25_H1_PP_BIT;
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
v |= MX25_H1_OCPOL_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX25_H1_TLL_BIT;
if (flags & MXC_EHCI_INTERNAL_PHY)
v |= MX25_H1_USBTE_BIT;
if (flags & MXC_EHCI_IPPUE_DOWN)
v |= MX25_H1_IPPUE_DOWN_BIT;
if (flags & MXC_EHCI_IPPUE_UP)
v |= MX25_H1_IPPUE_UP_BIT;
break;
default:
return -EINVAL;
}
#elif defined(CONFIG_MX31)
switch (port) {
case 0: /* OTG port */
v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_OTG_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_OTG_PM_BIT;
break;
case 1: /* H1 port */
v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | MX31_H1_DT_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H1_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_H1_PM_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX31_H1_DT_BIT;
break;
case 2: /* H2 port */
v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | MX31_H2_DT_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H2_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_H2_PM_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX31_H2_DT_BIT;
break;
default:
return -EINVAL;
}
#elif defined(CONFIG_MX35)
switch (port) {
case 0: /* OTG port */
v &= ~(MX35_OTG_SIC_MASK | MX35_OTG_PM_BIT | MX35_OTG_PP_BIT |
MX35_OTG_OCPOL_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_OTG_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX35_OTG_PM_BIT;
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MX35_OTG_PP_BIT;
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
v |= MX35_OTG_OCPOL_BIT;
break;
case 1: /* H1 port */
v &= ~(MX35_H1_SIC_MASK | MX35_H1_PM_BIT | MX35_H1_PP_BIT |
MX35_H1_OCPOL_BIT | MX35_H1_TLL_BIT |
MX35_H1_USBTE_BIT | MX35_H1_IPPUE_DOWN_BIT |
MX35_H1_IPPUE_UP_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_H1_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX35_H1_PM_BIT;
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
v |= MX35_H1_PP_BIT;
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
v |= MX35_H1_OCPOL_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX35_H1_TLL_BIT;
if (flags & MXC_EHCI_INTERNAL_PHY)
v |= MX35_H1_USBTE_BIT;
if (flags & MXC_EHCI_IPPUE_DOWN)
v |= MX35_H1_IPPUE_DOWN_BIT;
if (flags & MXC_EHCI_IPPUE_UP)
v |= MX35_H1_IPPUE_UP_BIT;
break;
default:
return -EINVAL;
}
#else
#error MXC EHCI USB driver not supported on this platform
#endif
#ifdef CONFIG_MX31
v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
switch (port) {
case 0: /* OTG port */
v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK)
<< MX31_OTG_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_OTG_PM_BIT;
break;
case 1: /* H1 port */
v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT |
MX31_H1_DT_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK)
<< MX31_H1_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_H1_PM_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX31_H1_DT_BIT;
break;
case 2: /* H2 port */
v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT |
MX31_H2_DT_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK)
<< MX31_H2_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_H2_PM_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX31_H2_DT_BIT;
break;
default:
return -EINVAL;
}
#endif
writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
return 0;
}
@@ -119,13 +234,17 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
udelay(80);
ehci = (struct usb_ehci *)(IMX_USB_BASE +
(0x200 * CONFIG_MXC_USB_PORT));
IMX_USB_PORT_OFFSET * CONFIG_MXC_USB_PORT);
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
setbits_le32(&ehci->usbmode, CM_HOST);
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
#ifdef CONFIG_MX35
/* Workaround for ENGcm11601 */
__raw_writel(0, &ehci->sbuscfg);
#endif
udelay(10000);

View File

@@ -37,6 +37,7 @@ COBJS-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o
COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o
COBJS-$(CONFIG_S6E63D6) += s6e63d6.o
COBJS-$(CONFIG_LD9040) += ld9040.o
COBJS-$(CONFIG_SED156X) += sed156x.o
COBJS-$(CONFIG_VIDEO_AMBA) += amba.o
COBJS-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o
@@ -50,6 +51,7 @@ COBJS-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o
COBJS-$(CONFIG_VIDEO_SM501) += sm501.o
COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o
COBJS-$(CONFIG_VIDEO_TEGRA) += tegra.o
COBJS-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
COBJS := $(sort $(COBJS-y))

View File

@@ -70,8 +70,19 @@ static void draw_logo(void)
int x, y;
ulong addr;
x = ((panel_width - panel_info.logo_width) >> 1);
y = ((panel_height - panel_info.logo_height) >> 1) - 4;
if (panel_width >= panel_info.logo_width) {
x = ((panel_width - panel_info.logo_width) >> 1);
} else {
x = 0;
printf("Warning: image width is bigger than display width\n");
}
if (panel_height >= panel_info.logo_height) {
y = ((panel_height - panel_info.logo_height) >> 1) - 4;
} else {
y = 0;
printf("Warning: image height is bigger than display height\n");
}
addr = panel_info.logo_addr;
bmp_display(addr, x, y);

View File

@@ -55,7 +55,7 @@
#define IPU_TPM_REG_BASE 0x01060000
#define IPU_DC_TMPL_REG_BASE 0x01080000
#define IPU_ISP_TBPR_REG_BASE 0x010C0000
#elif defined(CONFIG_MX6Q)
#elif defined(CONFIG_MX6)
#define IPU_CPMEM_REG_BASE 0x00100000
#define IPU_LUT_REG_BASE 0x00120000
#define IPU_SRM_REG_BASE 0x00140000

144
drivers/video/ld9040.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* ld9040 AMOLED LCD panel driver.
*
* Copyright (C) 2012 Samsung Electronics
* Donghwa Lee <dh09.lee@samsung.com>
*
* 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.
*
* 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 <spi.h>
static const unsigned char SEQ_SWRESET[] = {
0x01,
};
static const unsigned char SEQ_USER_SETTING[] = {
0xF0, 0x5A, 0x5A
};
static const unsigned char SEQ_ELVSS_ON[] = {
0xB1, 0x0D, 0x00, 0x16,
};
static const unsigned char SEQ_TEMP_SWIRE[] = {
0xB2, 0x06, 0x06, 0x06, 0x06,
};
static const unsigned char SEQ_GTCON[] = {
0xF7, 0x09, 0x00, 0x00,
};
static const unsigned char SEQ_PANEL_CONDITION[] = {
0xF8, 0x05, 0x65, 0x96, 0x71, 0x7D, 0x19, 0x3B,
0x0D, 0x19, 0x7E, 0x0D, 0xE2, 0x00, 0x00, 0x7E,
0x7D, 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02,
};
static const unsigned char SEQ_GAMMA_SET1[] = {
0xF9, 0x00, 0xA7, 0xB4, 0xAE, 0xBF, 0x00, 0x91,
0x00, 0xB2, 0xB4, 0xAA, 0xBB, 0x00, 0xAC, 0x00,
0xB3, 0xB1, 0xAA, 0xBC, 0x00, 0xB3,
};
static const unsigned char SEQ_GAMMA_CTRL[] = {
0xFB, 0x02, 0x5A,
};
static const unsigned char SEQ_APON[] = {
0xF3, 0x00, 0x00, 0x00, 0x0A, 0x02,
};
static const unsigned char SEQ_DISPCTL[] = {
0xF2, 0x02, 0x08, 0x08, 0x10, 0x10,
};
static const unsigned char SEQ_MANPWR[] = {
0xB0, 0x04,
};
static const unsigned char SEQ_PWR_CTRL[] = {
0xF4, 0x0A, 0x87, 0x25, 0x6A, 0x44, 0x02, 0x88,
};
static const unsigned char SEQ_SLPOUT[] = {
0x11,
};
static const unsigned char SEQ_SLPIN[] = {
0x10,
};
static const unsigned char SEQ_DISPON[] = {
0x29,
};
static const unsigned char SEQ_DISPOFF[] = {
0x28,
};
static void ld9040_spi_write(const unsigned char *wbuf, unsigned int size_cmd)
{
int i = 0;
/*
* Data are transmitted in 9-bit words:
* the first bit is command/parameter, the other are the value.
* The value's LSB is shifted to MSB position, to be sent as 9th bit
*/
unsigned int data_out = 0, data_in = 0;
for (i = 0; i < size_cmd; i++) {
data_out = wbuf[i] >> 1;
if (i != 0)
data_out += 0x0080;
if (wbuf[i] & 0x01)
data_out += 0x8000;
spi_xfer(NULL, 9, &data_out, &data_in, SPI_XFER_BEGIN);
}
}
void ld9040_cfg_ldo(void)
{
udelay(10);
ld9040_spi_write(SEQ_USER_SETTING,
ARRAY_SIZE(SEQ_USER_SETTING));
ld9040_spi_write(SEQ_PANEL_CONDITION,
ARRAY_SIZE(SEQ_PANEL_CONDITION));
ld9040_spi_write(SEQ_DISPCTL, ARRAY_SIZE(SEQ_DISPCTL));
ld9040_spi_write(SEQ_MANPWR, ARRAY_SIZE(SEQ_MANPWR));
ld9040_spi_write(SEQ_PWR_CTRL, ARRAY_SIZE(SEQ_PWR_CTRL));
ld9040_spi_write(SEQ_ELVSS_ON, ARRAY_SIZE(SEQ_ELVSS_ON));
ld9040_spi_write(SEQ_GTCON, ARRAY_SIZE(SEQ_GTCON));
ld9040_spi_write(SEQ_GAMMA_SET1, ARRAY_SIZE(SEQ_GAMMA_SET1));
ld9040_spi_write(SEQ_GAMMA_CTRL, ARRAY_SIZE(SEQ_GAMMA_CTRL));
ld9040_spi_write(SEQ_SLPOUT, ARRAY_SIZE(SEQ_SLPOUT));
udelay(120);
}
void ld9040_enable_ldo(unsigned int onoff)
{
if (onoff)
ld9040_spi_write(SEQ_DISPON, ARRAY_SIZE(SEQ_DISPON));
else
ld9040_spi_write(SEQ_DISPOFF, ARRAY_SIZE(SEQ_DISPOFF));
}

379
drivers/video/tegra.c Normal file
View File

@@ -0,0 +1,379 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* 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.
*
* 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 <fdtdec.h>
#include <lcd.h>
#include <asm/system.h>
#include <asm/gpio.h>
#include <asm/arch/clock.h>
#include <asm/arch/funcmux.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/pwm.h>
#include <asm/arch/display.h>
#include <asm/arch-tegra/timer.h>
DECLARE_GLOBAL_DATA_PTR;
/* These are the stages we go throuh in enabling the LCD */
enum stage_t {
STAGE_START,
STAGE_PANEL_VDD,
STAGE_LVDS,
STAGE_BACKLIGHT_VDD,
STAGE_PWM,
STAGE_BACKLIGHT_EN,
STAGE_DONE,
};
static enum stage_t stage; /* Current stage we are at */
static unsigned long timer_next; /* Time we can move onto next stage */
/* Our LCD config, set up in handle_stage() */
static struct fdt_panel_config config;
struct fdt_disp_config *disp_config; /* Display controller config */
enum {
/* Maximum LCD size we support */
LCD_MAX_WIDTH = 1366,
LCD_MAX_HEIGHT = 768,
LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
};
int lcd_line_length;
int lcd_color_fg;
int lcd_color_bg;
void *lcd_base; /* Start of framebuffer memory */
void *lcd_console_address; /* Start of console buffer */
short console_col;
short console_row;
vidinfo_t panel_info = {
/* Insert a value here so that we don't end up in the BSS */
.vl_col = -1,
};
char lcd_cursor_enabled;
ushort lcd_cursor_width;
ushort lcd_cursor_height;
#ifndef CONFIG_OF_CONTROL
#error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support"
#endif
void lcd_cursor_size(ushort width, ushort height)
{
lcd_cursor_width = width;
lcd_cursor_height = height;
}
void lcd_toggle_cursor(void)
{
ushort x, y;
uchar *dest;
ushort row;
x = console_col * lcd_cursor_width;
y = console_row * lcd_cursor_height;
dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_BPP) /
8);
for (row = 0; row < lcd_cursor_height; ++row, dest += lcd_line_length) {
ushort *d = (ushort *)dest;
ushort color;
int i;
for (i = 0; i < lcd_cursor_width; ++i) {
color = *d;
color ^= lcd_color_fg;
*d = color;
++d;
}
}
}
void lcd_cursor_on(void)
{
lcd_cursor_enabled = 1;
lcd_toggle_cursor();
}
void lcd_cursor_off(void)
{
lcd_cursor_enabled = 0;
lcd_toggle_cursor();
}
char lcd_is_cursor_enabled(void)
{
return lcd_cursor_enabled;
}
static void update_panel_size(struct fdt_disp_config *config)
{
panel_info.vl_col = config->width;
panel_info.vl_row = config->height;
panel_info.vl_bpix = config->log2_bpp;
}
/*
* Main init function called by lcd driver.
* Inits and then prints test pattern if required.
*/
void lcd_ctrl_init(void *lcdbase)
{
int line_length, size;
int type = DCACHE_OFF;
assert(disp_config);
lcd_base = (void *)disp_config->frame_buffer;
/* Make sure that we can acommodate the selected LCD */
assert(disp_config->width <= LCD_MAX_WIDTH);
assert(disp_config->height <= LCD_MAX_HEIGHT);
assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP);
if (disp_config->width <= LCD_MAX_WIDTH
&& disp_config->height <= LCD_MAX_HEIGHT
&& disp_config->log2_bpp <= LCD_MAX_LOG2_BPP)
update_panel_size(disp_config);
size = lcd_get_size(&line_length);
/* Set up the LCD caching as requested */
if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH)
type = DCACHE_WRITETHROUGH;
else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK)
type = DCACHE_WRITEBACK;
mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type);
/* Enable flushing after LCD writes if requested */
lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH);
debug("LCD frame buffer at %p\n", lcd_base);
}
ulong calc_fbsize(void)
{
return (panel_info.vl_col * panel_info.vl_row *
NBITS(panel_info.vl_bpix)) / 8;
}
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
{
}
void tegra_lcd_early_init(const void *blob)
{
/*
* Go with the maximum size for now. We will fix this up after
* relocation. These values are only used for memory alocation.
*/
panel_info.vl_col = LCD_MAX_WIDTH;
panel_info.vl_row = LCD_MAX_HEIGHT;
panel_info.vl_bpix = LCD_MAX_LOG2_BPP;
}
/**
* Decode the panel information from the fdt.
*
* @param blob fdt blob
* @param config structure to store fdt config into
* @return 0 if ok, -ve on error
*/
static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config)
{
int display_node;
disp_config = tegra_display_get_config();
if (!disp_config) {
debug("%s: Display controller is not configured\n", __func__);
return -1;
}
display_node = disp_config->panel_node;
if (display_node < 0) {
debug("%s: No panel configuration available\n", __func__);
return -1;
}
config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm");
if (config->pwm_channel < 0) {
debug("%s: Unable to request PWM channel\n", __func__);
return -1;
}
config->cache_type = fdtdec_get_int(blob, display_node,
"nvidia,cache-type",
FDT_LCD_CACHE_WRITE_BACK_FLUSH);
/* These GPIOs are all optional */
fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios",
&config->backlight_en);
fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios",
&config->lvds_shutdown);
fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios",
&config->backlight_vdd);
fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios",
&config->panel_vdd);
return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings",
config->panel_timings, FDT_LCD_TIMINGS);
}
/**
* Handle the next stage of device init
*/
static int handle_stage(const void *blob)
{
debug("%s: stage %d\n", __func__, stage);
/* do the things for this stage */
switch (stage) {
case STAGE_START:
/* Initialize the Tegra display controller */
if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) {
printf("%s: Failed to probe display driver\n",
__func__);
return -1;
}
/* get panel details */
if (fdt_decode_lcd(blob, &config)) {
printf("No valid LCD information in device tree\n");
return -1;
}
/*
* It is possible that the FDT has requested that the LCD be
* disabled. We currently don't support this. It would require
* changes to U-Boot LCD subsystem to have LCD support
* compiled in but not used. An easier option might be to
* still have a frame buffer, but leave the backlight off and
* remove all mention of lcd in the stdout environment
* variable.
*/
funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
fdtdec_setup_gpio(&config.panel_vdd);
fdtdec_setup_gpio(&config.lvds_shutdown);
fdtdec_setup_gpio(&config.backlight_vdd);
fdtdec_setup_gpio(&config.backlight_en);
/*
* TODO: If fdt includes output flag we can omit this code
* since fdtdec_setup_gpio will do it for us.
*/
if (fdt_gpio_isvalid(&config.panel_vdd))
gpio_direction_output(config.panel_vdd.gpio, 0);
if (fdt_gpio_isvalid(&config.lvds_shutdown))
gpio_direction_output(config.lvds_shutdown.gpio, 0);
if (fdt_gpio_isvalid(&config.backlight_vdd))
gpio_direction_output(config.backlight_vdd.gpio, 0);
if (fdt_gpio_isvalid(&config.backlight_en))
gpio_direction_output(config.backlight_en.gpio, 0);
break;
case STAGE_PANEL_VDD:
if (fdt_gpio_isvalid(&config.panel_vdd))
gpio_direction_output(config.panel_vdd.gpio, 1);
break;
case STAGE_LVDS:
if (fdt_gpio_isvalid(&config.lvds_shutdown))
gpio_set_value(config.lvds_shutdown.gpio, 1);
break;
case STAGE_BACKLIGHT_VDD:
if (fdt_gpio_isvalid(&config.backlight_vdd))
gpio_set_value(config.backlight_vdd.gpio, 1);
break;
case STAGE_PWM:
/* Enable PWM at 15/16 high, 32768 Hz with divider 1 */
pinmux_set_func(PINGRP_GPU, PMUX_FUNC_PWM);
pinmux_tristate_disable(PINGRP_GPU);
pwm_enable(config.pwm_channel, 32768, 0xdf, 1);
break;
case STAGE_BACKLIGHT_EN:
if (fdt_gpio_isvalid(&config.backlight_en))
gpio_set_value(config.backlight_en.gpio, 1);
break;
case STAGE_DONE:
break;
}
/* set up timer for next stage */
timer_next = timer_get_us();
if (stage < FDT_LCD_TIMINGS)
timer_next += config.panel_timings[stage] * 1000;
/* move to next stage */
stage++;
return 0;
}
int tegra_lcd_check_next_stage(const void *blob, int wait)
{
if (stage == STAGE_DONE)
return 0;
do {
/* wait if we need to */
debug("%s: stage %d\n", __func__, stage);
if (stage != STAGE_START) {
int delay = timer_next - timer_get_us();
if (delay > 0) {
if (wait)
udelay(delay);
else
return 0;
}
}
if (handle_stage(blob))
return -1;
} while (wait && stage != STAGE_DONE);
if (stage == STAGE_DONE)
debug("%s: LCD init complete\n", __func__);
return 0;
}
void lcd_enable(void)
{
/*
* Backlight and power init will be done separately in
* tegra_lcd_check_next_stage(), which should be called in
* board_late_init().
*
* U-Boot code supports only colour depth, selected at compile time.
* The device tree setting should match this. Otherwise the display
* will not look right, and U-Boot may crash.
*/
if (disp_config->log2_bpp != LCD_BPP) {
printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)"
" must match setting of LCD_BPP (%d)\n", __func__,
disp_config->log2_bpp, disp_config->bpp, LCD_BPP);
}
}

View File

@@ -28,6 +28,7 @@ LIB := $(obj)libwatchdog.o
COBJS-$(CONFIG_AT91SAM9_WATCHDOG) += at91sam9_wdt.o
COBJS-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
COBJS-$(CONFIG_TNETV107X_WATCHDOG) += tnetv107x_wdt.o
COBJS-$(CONFIG_S5P) += s5p_wdt.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2012 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*
* 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.
*
* 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 <asm/io.h>
#include <asm/arch/watchdog.h>
#define PRESCALER_VAL 255
void wdt_stop(void)
{
struct s5p_watchdog *wdt =
(struct s5p_watchdog *)samsung_get_base_watchdog();
unsigned int wtcon;
wtcon = readl(&wdt->wtcon);
wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET);
writel(wtcon, &wdt->wtcon);
}
void wdt_start(unsigned int timeout)
{
struct s5p_watchdog *wdt =
(struct s5p_watchdog *)samsung_get_base_watchdog();
unsigned int wtcon;
wdt_stop();
wtcon = readl(&wdt->wtcon);
wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128));
wtcon &= ~WTCON_INT;
wtcon |= WTCON_RESET;
wtcon |= WTCON_PRESCALER(PRESCALER_VAL);
writel(timeout, &wdt->wtdat);
writel(timeout, &wdt->wtcnt);
writel(wtcon, &wdt->wtcon);
}