summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/gpio-uclass.c38
-rw-r--r--drivers/gpio/stm32_gpio.c18
-rw-r--r--drivers/i2c/i2c-gpio.c13
-rw-r--r--drivers/i2c/i2c-uclass.c26
-rw-r--r--drivers/i2c/mxc_i2c.c581
-rw-r--r--drivers/i2c/sandbox_i2c.c54
-rw-r--r--drivers/misc/Kconfig13
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/cros_ec.c3
-rw-r--r--drivers/misc/cros_ec_sandbox.c2
-rw-r--r--drivers/misc/cros_ec_spi.c23
-rw-r--r--drivers/misc/i2c_eeprom_emul.c1
-rw-r--r--drivers/misc/pca9551_led.c155
-rw-r--r--drivers/misc/swap_case.c1
-rw-r--r--drivers/mtd/spi/sandbox.c2
-rw-r--r--drivers/mtd/spi/sf-uclass.c4
-rw-r--r--drivers/net/sandbox.c17
-rw-r--r--drivers/power/Kconfig8
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/as3722.c16
-rw-r--r--drivers/power/pmic/Kconfig43
-rw-r--r--drivers/power/pmic/Makefile3
-rw-r--r--drivers/power/pmic/i2c_pmic_emul.c142
-rw-r--r--drivers/power/pmic/max77686.c92
-rw-r--r--drivers/power/pmic/pmic-uclass.c145
-rw-r--r--drivers/power/pmic/pmic_max77686.c2
-rw-r--r--drivers/power/pmic/sandbox.c79
-rw-r--r--drivers/power/regulator/Kconfig63
-rw-r--r--drivers/power/regulator/Makefile11
-rw-r--r--drivers/power/regulator/fixed.c126
-rw-r--r--drivers/power/regulator/max77686.c825
-rw-r--r--drivers/power/regulator/regulator-uclass.c332
-rw-r--r--drivers/power/regulator/sandbox.c355
-rw-r--r--drivers/pwm/pwm-imx-util.c2
-rw-r--r--drivers/rtc/Kconfig8
-rw-r--r--drivers/rtc/Makefile4
-rw-r--r--drivers/rtc/at91sam9_rtt.c5
-rw-r--r--drivers/rtc/bfin_rtc.c5
-rw-r--r--drivers/rtc/date.c38
-rw-r--r--drivers/rtc/ds1306.c5
-rw-r--r--drivers/rtc/ds1374.c6
-rw-r--r--drivers/rtc/ftrtc010.c5
-rw-r--r--drivers/rtc/i2c_rtc_emul.c236
-rw-r--r--drivers/rtc/imxdi.c5
-rw-r--r--drivers/rtc/mc13xxx-rtc.c5
-rw-r--r--drivers/rtc/mcfrtc.c2
-rw-r--r--drivers/rtc/mpc8xx.c5
-rw-r--r--drivers/rtc/mx27rtc.c5
-rw-r--r--drivers/rtc/mxsrtc.c5
-rw-r--r--drivers/rtc/pl031.c5
-rw-r--r--drivers/rtc/rtc-uclass.c96
-rw-r--r--drivers/rtc/sandbox_rtc.c106
-rw-r--r--drivers/serial/serial_pl01x.c30
-rw-r--r--drivers/serial/serial_stm32.c72
-rw-r--r--drivers/spi/spi-uclass.c9
-rw-r--r--drivers/tpm/tpm.c89
-rw-r--r--drivers/tpm/tpm_private.h3
-rw-r--r--drivers/tpm/tpm_tis_i2c.c134
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-exynos.c117
-rw-r--r--drivers/usb/host/ehci-hcd.c128
-rw-r--r--drivers/usb/host/ehci-sunxi.c93
-rw-r--r--drivers/usb/host/ohci-hcd.c669
-rw-r--r--drivers/usb/host/ohci-sunxi.c104
-rw-r--r--drivers/usb/host/ohci.h143
-rw-r--r--drivers/usb/host/usb-uclass.c180
-rw-r--r--drivers/usb/host/xhci-exynos5.c108
-rw-r--r--drivers/video/Kconfig15
-rw-r--r--drivers/video/Makefile6
-rw-r--r--drivers/video/dp-uclass.c34
-rw-r--r--drivers/video/tegra124/Makefile10
-rw-r--r--drivers/video/tegra124/display.c472
-rw-r--r--drivers/video/tegra124/displayport.h412
-rw-r--r--drivers/video/tegra124/dp.c1607
-rw-r--r--drivers/video/tegra124/sor.c1024
-rw-r--r--drivers/video/tegra124/sor.h922
-rw-r--r--drivers/video/tegra124/tegra124-lcd.c97
77 files changed, 9275 insertions, 947 deletions
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 381868b..530bb3e 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -495,22 +495,54 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
return 0;
}
+int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
+{
+ int i, ret;
+ int gpio;
+
+ for (i = 0; i < 32; i++) {
+ gpio = gpio_num_array[i];
+ if (gpio == -1)
+ break;
+ ret = gpio_requestf(gpio, fmt, i);
+ if (ret)
+ goto err;
+ ret = gpio_direction_input(gpio);
+ if (ret) {
+ gpio_free(gpio);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ for (i--; i >= 0; i--)
+ gpio_free(gpio_num_array[i]);
+
+ return ret;
+}
+
/*
* get a number comprised of multiple GPIO values. gpio_num_array points to
* the array of gpio pin numbers to scan, terminated by -1.
*/
-unsigned gpio_get_values_as_int(const int *gpio_num_array)
+int gpio_get_values_as_int(const int *gpio_list)
{
int gpio;
unsigned bitmask = 1;
unsigned vector = 0;
+ int ret;
while (bitmask &&
- ((gpio = *gpio_num_array++) != -1)) {
- if (gpio_get_value(gpio))
+ ((gpio = *gpio_list++) != -1)) {
+ ret = gpio_get_value(gpio);
+ if (ret < 0)
+ return ret;
+ else if (ret)
vector |= bitmask;
bitmask <<= 1;
}
+
return vector;
}
diff --git a/drivers/gpio/stm32_gpio.c b/drivers/gpio/stm32_gpio.c
index d3497e9..6d6fdb0 100644
--- a/drivers/gpio/stm32_gpio.c
+++ b/drivers/gpio/stm32_gpio.c
@@ -69,22 +69,14 @@ int stm32_gpio_config(const struct stm32_gpio_dsc *dsc,
setbits_le32(&STM32_RCC->ahb1enr, 1 << dsc->port);
i = (dsc->pin & 0x07) * 4;
- clrbits_le32(&gpio_regs->afr[dsc->pin >> 3], (0xF << i));
- setbits_le32(&gpio_regs->afr[dsc->pin >> 3], ctl->af << i);
+ clrsetbits_le32(&gpio_regs->afr[dsc->pin >> 3], 0xF << i, ctl->af << i);
i = dsc->pin * 2;
- clrbits_le32(&gpio_regs->moder, (0x3 << i));
- setbits_le32(&gpio_regs->moder, ctl->mode << i);
-
- clrbits_le32(&gpio_regs->otyper, (0x3 << i));
- setbits_le32(&gpio_regs->otyper, ctl->otype << i);
-
- clrbits_le32(&gpio_regs->ospeedr, (0x3 << i));
- setbits_le32(&gpio_regs->ospeedr, ctl->speed << i);
-
- clrbits_le32(&gpio_regs->pupdr, (0x3 << i));
- setbits_le32(&gpio_regs->pupdr, ctl->pupd << i);
+ clrsetbits_le32(&gpio_regs->moder, 0x3 << i, ctl->mode << i);
+ clrsetbits_le32(&gpio_regs->otyper, 0x3 << i, ctl->otype << i);
+ clrsetbits_le32(&gpio_regs->ospeedr, 0x3 << i, ctl->speed << i);
+ clrsetbits_le32(&gpio_regs->pupdr, 0x3 << i, ctl->pupd << i);
rv = 0;
out:
diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c
index ed899d4..a8b83c5 100644
--- a/drivers/i2c/i2c-gpio.c
+++ b/drivers/i2c/i2c-gpio.c
@@ -41,18 +41,19 @@ static int i2c_gpio_sda_get(struct gpio_desc *sda)
static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit)
{
- if (bit) {
+ if (bit)
dm_gpio_set_dir_flags(sda, GPIOD_IS_IN);
- } else {
+ else
dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT);
- dm_gpio_set_value(sda, 0);
- }
}
static void i2c_gpio_scl_set(struct gpio_desc *scl, int bit)
{
- dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT);
- dm_gpio_set_value(scl, bit);
+ ulong flags = GPIOD_IS_OUT;
+
+ if (bit)
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ dm_gpio_set_dir_flags(scl, flags);
}
static void i2c_gpio_write_bit(struct gpio_desc *scl, struct gpio_desc *sda,
diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index f2e95c0..eaba965 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -186,6 +186,25 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,
}
}
+int dm_i2c_reg_read(struct udevice *dev, uint offset)
+{
+ uint8_t val;
+ int ret;
+
+ ret = dm_i2c_read(dev, offset, &val, 1);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value)
+{
+ uint8_t val = value;
+
+ return dm_i2c_write(dev, offset, &val, 1);
+}
+
/**
* i2c_probe_chip() - probe for a chip on a bus
*
@@ -396,6 +415,13 @@ int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len)
return 0;
}
+int i2c_get_chip_offset_len(struct udevice *dev)
+{
+ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+
+ return chip->offset_len;
+}
+
int i2c_deblock(struct udevice *bus)
{
struct dm_i2c_ops *ops = i2c_get_ops(bus);
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c
index 42782cb..81adf6f 100644
--- a/drivers/i2c/mxc_i2c.c
+++ b/drivers/i2c/mxc_i2c.c
@@ -18,29 +18,25 @@
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/errno.h>
+#include <asm/imx-common/mxc_i2c.h>
#include <asm/io.h>
#include <i2c.h>
#include <watchdog.h>
+#include <dm.h>
+#include <fdtdec.h>
DECLARE_GLOBAL_DATA_PTR;
-#ifdef I2C_QUIRK_REG
-struct mxc_i2c_regs {
- uint8_t iadr;
- uint8_t ifdr;
- uint8_t i2cr;
- uint8_t i2sr;
- uint8_t i2dr;
-};
-#else
-struct mxc_i2c_regs {
- uint32_t iadr;
- uint32_t ifdr;
- uint32_t i2cr;
- uint32_t i2sr;
- uint32_t i2dr;
-};
-#endif
+#define I2C_QUIRK_FLAG (1 << 0)
+
+#define IMX_I2C_REGSHIFT 2
+#define VF610_I2C_REGSHIFT 0
+/* Register index */
+#define IADR 0
+#define IFDR 1
+#define I2CR 2
+#define I2SR 3
+#define I2DR 4
#define I2CR_IIEN (1 << 6)
#define I2CR_MSTA (1 << 5)
@@ -104,7 +100,6 @@ static u16 i2c_clk_div[50][2] = {
};
#endif
-
#ifndef CONFIG_SYS_MXC_I2C1_SPEED
#define CONFIG_SYS_MXC_I2C1_SPEED 100000
#endif
@@ -131,11 +126,10 @@ static u16 i2c_clk_div[50][2] = {
#define CONFIG_SYS_MXC_I2C4_SLAVE 0
#endif
-
/*
* Calculate and set proper clock divider
*/
-static uint8_t i2c_imx_get_clk(unsigned int rate)
+static uint8_t i2c_imx_get_clk(struct mxc_i2c_bus *i2c_bus, unsigned int rate)
{
unsigned int i2c_clk_rate;
unsigned int div;
@@ -168,18 +162,20 @@ static uint8_t i2c_imx_get_clk(unsigned int rate)
/*
* Set I2C Bus speed
*/
-static int bus_i2c_set_bus_speed(void *base, int speed)
+static int bus_i2c_set_bus_speed(struct mxc_i2c_bus *i2c_bus, int speed)
{
- struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base;
- u8 clk_idx = i2c_imx_get_clk(speed);
+ ulong base = i2c_bus->base;
+ bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
+ u8 clk_idx = i2c_imx_get_clk(i2c_bus, speed);
u8 idx = i2c_clk_div[clk_idx][1];
+ int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
/* Store divider value */
- writeb(idx, &i2c_regs->ifdr);
+ writeb(idx, base + (IFDR << reg_shift));
/* Reset module */
- writeb(I2CR_IDIS, &i2c_regs->i2cr);
- writeb(0, &i2c_regs->i2sr);
+ writeb(I2CR_IDIS, base + (I2CR << reg_shift));
+ writeb(0, base + (I2SR << reg_shift));
return 0;
}
@@ -187,21 +183,26 @@ static int bus_i2c_set_bus_speed(void *base, int speed)
#define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8))
#define ST_IIF (I2SR_IIF | (I2SR_IIF << 8))
-static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state)
+static int wait_for_sr_state(struct mxc_i2c_bus *i2c_bus, unsigned state)
{
unsigned sr;
ulong elapsed;
+ bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
+ int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+ ulong base = i2c_bus->base;
ulong start_time = get_timer(0);
for (;;) {
- sr = readb(&i2c_regs->i2sr);
+ sr = readb(base + (I2SR << reg_shift));
if (sr & I2SR_IAL) {
-#ifdef I2C_QUIRK_REG
- writeb(sr | I2SR_IAL, &i2c_regs->i2sr);
-#else
- writeb(sr & ~I2SR_IAL, &i2c_regs->i2sr);
-#endif
+ if (quirk)
+ writeb(sr | I2SR_IAL, base +
+ (I2SR << reg_shift));
+ else
+ writeb(sr & ~I2SR_IAL, base +
+ (I2SR << reg_shift));
printf("%s: Arbitration lost sr=%x cr=%x state=%x\n",
- __func__, sr, readb(&i2c_regs->i2cr), state);
+ __func__, sr, readb(base + (I2CR << reg_shift)),
+ state);
return -ERESTART;
}
if ((sr & (state >> 8)) == (unsigned char)state)
@@ -212,17 +213,21 @@ static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state)
break;
}
printf("%s: failed sr=%x cr=%x state=%x\n", __func__,
- sr, readb(&i2c_regs->i2cr), state);
+ sr, readb(base + (I2CR << reg_shift)), state);
return -ETIMEDOUT;
}
-static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte)
+static int tx_byte(struct mxc_i2c_bus *i2c_bus, u8 byte)
{
int ret;
+ int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
+ VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+ ulong base = i2c_bus->base;
- writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
- writeb(byte, &i2c_regs->i2dr);
- ret = wait_for_sr_state(i2c_regs, ST_IIF);
+ writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
+ writeb(byte, base + (I2DR << reg_shift));
+
+ ret = wait_for_sr_state(i2c_bus, ST_IIF);
if (ret < 0)
return ret;
if (ret & I2SR_RX_NO_AK)
@@ -231,16 +236,28 @@ static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte)
}
/*
+ * Stub implementations for outer i2c slave operations.
+ */
+void __i2c_force_reset_slave(void)
+{
+}
+void i2c_force_reset_slave(void)
+ __attribute__((weak, alias("__i2c_force_reset_slave")));
+
+/*
* Stop I2C transaction
*/
-static void i2c_imx_stop(struct mxc_i2c_regs *i2c_regs)
+static void i2c_imx_stop(struct mxc_i2c_bus *i2c_bus)
{
int ret;
- unsigned int temp = readb(&i2c_regs->i2cr);
+ int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
+ VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+ ulong base = i2c_bus->base;
+ unsigned int temp = readb(base + (I2CR << reg_shift));
temp &= ~(I2CR_MSTA | I2CR_MTX);
- writeb(temp, &i2c_regs->i2cr);
- ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE);
+ writeb(temp, base + (I2CR << reg_shift));
+ ret = wait_for_sr_state(i2c_bus, ST_BUS_IDLE);
if (ret < 0)
printf("%s:trigger stop failed\n", __func__);
}
@@ -249,66 +266,96 @@ static void i2c_imx_stop(struct mxc_i2c_regs *i2c_regs)
* Send start signal, chip address and
* write register address
*/
-static int i2c_init_transfer_(struct mxc_i2c_regs *i2c_regs,
- uchar chip, uint addr, int alen)
+static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip,
+ u32 addr, int alen)
{
unsigned int temp;
int ret;
+ bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
+ ulong base = i2c_bus->base;
+ int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+
+ /* Reset i2c slave */
+ i2c_force_reset_slave();
/* Enable I2C controller */
-#ifdef I2C_QUIRK_REG
- if (readb(&i2c_regs->i2cr) & I2CR_IDIS) {
-#else
- if (!(readb(&i2c_regs->i2cr) & I2CR_IEN)) {
-#endif
- writeb(I2CR_IEN, &i2c_regs->i2cr);
+ if (quirk)
+ ret = readb(base + (I2CR << reg_shift)) & I2CR_IDIS;
+ else
+ ret = !(readb(base + (I2CR << reg_shift)) & I2CR_IEN);
+
+ if (ret) {
+ writeb(I2CR_IEN, base + (I2CR << reg_shift));
/* Wait for controller to be stable */
udelay(50);
}
- if (readb(&i2c_regs->iadr) == (chip << 1))
- writeb((chip << 1) ^ 2, &i2c_regs->iadr);
- writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
- ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE);
+
+ if (readb(base + (IADR << reg_shift)) == (chip << 1))
+ writeb((chip << 1) ^ 2, base + (IADR << reg_shift));
+ writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
+ ret = wait_for_sr_state(i2c_bus, ST_BUS_IDLE);
if (ret < 0)
return ret;
/* Start I2C transaction */
- temp = readb(&i2c_regs->i2cr);
+ temp = readb(base + (I2CR << reg_shift));
temp |= I2CR_MSTA;
- writeb(temp, &i2c_regs->i2cr);
+ writeb(temp, base + (I2CR << reg_shift));
- ret = wait_for_sr_state(i2c_regs, ST_BUS_BUSY);
+ ret = wait_for_sr_state(i2c_bus, ST_BUS_BUSY);
if (ret < 0)
return ret;
temp |= I2CR_MTX | I2CR_TX_NO_AK;
- writeb(temp, &i2c_regs->i2cr);
+ writeb(temp, base + (I2CR << reg_shift));
/* write slave address */
- ret = tx_byte(i2c_regs, chip << 1);
+ ret = tx_byte(i2c_bus, chip << 1);
if (ret < 0)
return ret;
while (alen--) {
- ret = tx_byte(i2c_regs, (addr >> (alen * 8)) & 0xff);
+ ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff);
if (ret < 0)
return ret;
}
return 0;
}
-static int i2c_idle_bus(void *base);
+#ifndef CONFIG_DM_I2C
+int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus)
+{
+ if (i2c_bus && i2c_bus->idle_bus_fn)
+ return i2c_bus->idle_bus_fn(i2c_bus->idle_bus_data);
+ return 0;
+}
+#else
+/*
+ * Since pinmux is not supported, implement a weak function here.
+ * You can implement your i2c_bus_idle in board file. When pinctrl
+ * is supported, this can be removed.
+ */
+int __i2c_idle_bus(struct mxc_i2c_bus *i2c_bus)
+{
+ return 0;
+}
-static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs,
- uchar chip, uint addr, int alen)
+int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus)
+ __attribute__((weak, alias("__i2c_idle_bus")));
+#endif
+
+static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip,
+ u32 addr, int alen)
{
int retry;
int ret;
+ int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
+ VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
for (retry = 0; retry < 3; retry++) {
- ret = i2c_init_transfer_(i2c_regs, chip, addr, alen);
+ ret = i2c_init_transfer_(i2c_bus, chip, addr, alen);
if (ret >= 0)
return 0;
- i2c_imx_stop(i2c_regs);
+ i2c_imx_stop(i2c_bus);
if (ret == -ENODEV)
return ret;
@@ -316,54 +363,67 @@ static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs,
retry);
if (ret != -ERESTART)
/* Disable controller */
- writeb(I2CR_IDIS, &i2c_regs->i2cr);
+ writeb(I2CR_IDIS, i2c_bus->base + (I2CR << reg_shift));
udelay(100);
- if (i2c_idle_bus(i2c_regs) < 0)
+ if (i2c_idle_bus(i2c_bus) < 0)
break;
}
- printf("%s: give up i2c_regs=%p\n", __func__, i2c_regs);
+ printf("%s: give up i2c_regs=0x%lx\n", __func__, i2c_bus->base);
return ret;
}
-/*
- * Read data from I2C device
- */
-int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf,
- int len)
+
+static int i2c_write_data(struct mxc_i2c_bus *i2c_bus, u8 chip, const u8 *buf,
+ int len)
+{
+ int i, ret = 0;
+
+ debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len);
+ debug("write_data: ");
+ /* use rc for counter */
+ for (i = 0; i < len; ++i)
+ debug(" 0x%02x", buf[i]);
+ debug("\n");
+
+ for (i = 0; i < len; i++) {
+ ret = tx_byte(i2c_bus, buf[i]);
+ if (ret < 0) {
+ debug("i2c_write_data(): rc=%d\n", ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf,
+ int len)
{
int ret;
unsigned int temp;
int i;
- struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base;
-
- ret = i2c_init_transfer(i2c_regs, chip, addr, alen);
- if (ret < 0)
- return ret;
+ int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
+ VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+ ulong base = i2c_bus->base;
- temp = readb(&i2c_regs->i2cr);
- temp |= I2CR_RSTA;
- writeb(temp, &i2c_regs->i2cr);
-
- ret = tx_byte(i2c_regs, (chip << 1) | 1);
- if (ret < 0) {
- i2c_imx_stop(i2c_regs);
- return ret;
- }
+ debug("i2c_read_data: chip=0x%x, len=0x%x\n", chip, len);
/* setup bus to read data */
- temp = readb(&i2c_regs->i2cr);
+ temp = readb(base + (I2CR << reg_shift));
temp &= ~(I2CR_MTX | I2CR_TX_NO_AK);
if (len == 1)
temp |= I2CR_TX_NO_AK;
- writeb(temp, &i2c_regs->i2cr);
- writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
- readb(&i2c_regs->i2dr); /* dummy read to clear ICF */
+ writeb(temp, base + (I2CR << reg_shift));
+ writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
+ /* dummy read to clear ICF */
+ readb(base + (I2DR << reg_shift));
/* read data */
for (i = 0; i < len; i++) {
- ret = wait_for_sr_state(i2c_regs, ST_IIF);
+ ret = wait_for_sr_state(i2c_bus, ST_IIF);
if (ret < 0) {
- i2c_imx_stop(i2c_regs);
+ debug("i2c_read_data(): ret=%d\n", ret);
+ i2c_imx_stop(i2c_bus);
return ret;
}
@@ -372,105 +432,111 @@ int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf,
* controller from generating another clock cycle
*/
if (i == (len - 1)) {
- i2c_imx_stop(i2c_regs);
+ i2c_imx_stop(i2c_bus);
} else if (i == (len - 2)) {
- temp = readb(&i2c_regs->i2cr);
+ temp = readb(base + (I2CR << reg_shift));
temp |= I2CR_TX_NO_AK;
- writeb(temp, &i2c_regs->i2cr);
+ writeb(temp, base + (I2CR << reg_shift));
}
- writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
- buf[i] = readb(&i2c_regs->i2dr);
+ writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
+ buf[i] = readb(base + (I2DR << reg_shift));
}
- i2c_imx_stop(i2c_regs);
+
+ /* reuse ret for counter*/
+ for (ret = 0; ret < len; ++ret)
+ debug(" 0x%02x", buf[ret]);
+ debug("\n");
+
+ i2c_imx_stop(i2c_bus);
return 0;
}
+#ifndef CONFIG_DM_I2C
/*
- * Write data to I2C device
+ * Read data from I2C device
*/
-int bus_i2c_write(void *base, uchar chip, uint addr, int alen,
- const uchar *buf, int len)
+static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
+ int alen, u8 *buf, int len)
{
- int ret;
- int i;
- struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base;
+ int ret = 0;
+ u32 temp;
+ int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
+ VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+ ulong base = i2c_bus->base;
- ret = i2c_init_transfer(i2c_regs, chip, addr, alen);
+ ret = i2c_init_transfer(i2c_bus, chip, addr, alen);
if (ret < 0)
return ret;
- for (i = 0; i < len; i++) {
- ret = tx_byte(i2c_regs, buf[i]);
- if (ret < 0)
- break;
+ temp = readb(base + (I2CR << reg_shift));
+ temp |= I2CR_RSTA;
+ writeb(temp, base + (I2CR << reg_shift));
+
+ ret = tx_byte(i2c_bus, (chip << 1) | 1);
+ if (ret < 0) {
+ i2c_imx_stop(i2c_bus);
+ return ret;
}
- i2c_imx_stop(i2c_regs);
+
+ ret = i2c_read_data(i2c_bus, chip, buf, len);
+
+ i2c_imx_stop(i2c_bus);
+ return ret;
+}
+
+/*
+ * Write data to I2C device
+ */
+static int bus_i2c_write(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
+ int alen, const u8 *buf, int len)
+{
+ int ret = 0;
+
+ ret = i2c_init_transfer(i2c_bus, chip, addr, alen);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_write_data(i2c_bus, chip, buf, len);
+
+ i2c_imx_stop(i2c_bus);
+
return ret;
}
-static void * const i2c_bases[] = {
+static struct mxc_i2c_bus mxc_i2c_buses[] = {
#if defined(CONFIG_MX25)
- (void *)IMX_I2C_BASE,
- (void *)IMX_I2C2_BASE,
- (void *)IMX_I2C3_BASE
+ { 0, IMX_I2C_BASE },
+ { 1, IMX_I2C2_BASE },
+ { 2, IMX_I2C3_BASE },
#elif defined(CONFIG_MX27)
- (void *)IMX_I2C1_BASE,
- (void *)IMX_I2C2_BASE
+ { 0, IMX_I2C1_BASE },
+ { 1, IMX_I2C2_BASE },
#elif defined(CONFIG_MX31) || defined(CONFIG_MX35) || \
defined(CONFIG_MX51) || defined(CONFIG_MX53) || \
- defined(CONFIG_MX6) || defined(CONFIG_LS102XA)
- (void *)I2C1_BASE_ADDR,
- (void *)I2C2_BASE_ADDR,
- (void *)I2C3_BASE_ADDR
+ defined(CONFIG_MX6)
+ { 0, I2C1_BASE_ADDR },
+ { 1, I2C2_BASE_ADDR },
+ { 2, I2C3_BASE_ADDR },
+#elif defined(CONFIG_LS102XA)
+ { 0, I2C1_BASE_ADDR, I2C_QUIRK_FLAG },
+ { 1, I2C2_BASE_ADDR, I2C_QUIRK_FLAG },
+ { 2, I2C3_BASE_ADDR, I2C_QUIRK_FLAG },
#elif defined(CONFIG_VF610)
- (void *)I2C0_BASE_ADDR
+ { 0, I2C0_BASE_ADDR, I2C_QUIRK_FLAG },
#elif defined(CONFIG_FSL_LSCH3)
- (void *)I2C1_BASE_ADDR,
- (void *)I2C2_BASE_ADDR,
- (void *)I2C3_BASE_ADDR,
- (void *)I2C4_BASE_ADDR
+ { 0, I2C1_BASE_ADDR, I2C_QUIRK_FLAG },
+ { 1, I2C2_BASE_ADDR, I2C_QUIRK_FLAG },
+ { 2, I2C3_BASE_ADDR, I2C_QUIRK_FLAG },
+ { 3, I2C4_BASE_ADDR, I2C_QUIRK_FLAG },
#else
#error "architecture not supported"
#endif
+ { }
};
-struct i2c_parms {
- void *base;
- void *idle_bus_data;
- int (*idle_bus_fn)(void *p);
-};
-
-struct sram_data {
- unsigned curr_i2c_bus;
- struct i2c_parms i2c_data[ARRAY_SIZE(i2c_bases)];
-};
-
-void *i2c_get_base(struct i2c_adapter *adap)
-{
- return i2c_bases[adap->hwadapnr];
-}
-
-static struct i2c_parms *i2c_get_parms(void *base)
-{
- struct sram_data *srdata = (void *)gd->srdata;
- int i = 0;
- struct i2c_parms *p = srdata->i2c_data;
- while (i < ARRAY_SIZE(srdata->i2c_data)) {
- if (p->base == base)
- return p;
- p++;
- i++;
- }
- printf("Invalid I2C base: %p\n", base);
- return NULL;
-}
-
-static int i2c_idle_bus(void *base)
+struct mxc_i2c_bus *i2c_get_base(struct i2c_adapter *adap)
{
- struct i2c_parms *p = i2c_get_parms(base);
- if (p && p->idle_bus_fn)
- return p->idle_bus_fn(p->idle_bus_data);
- return 0;
+ return &mxc_i2c_buses[adap->hwadapnr];
}
static int mxc_i2c_read(struct i2c_adapter *adap, uint8_t chip,
@@ -495,29 +561,33 @@ static int mxc_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
return bus_i2c_write(i2c_get_base(adap), chip, 0, 0, NULL, 0);
}
-void bus_i2c_init(void *base, int speed, int unused,
- int (*idle_bus_fn)(void *p), void *idle_bus_data)
+int __enable_i2c_clk(unsigned char enable, unsigned i2c_num)
+{
+ return 1;
+}
+int enable_i2c_clk(unsigned char enable, unsigned i2c_num)
+ __attribute__((weak, alias("__enable_i2c_clk")));
+
+void bus_i2c_init(int index, int speed, int unused,
+ int (*idle_bus_fn)(void *p), void *idle_bus_data)
{
- struct sram_data *srdata = (void *)gd->srdata;
- int i = 0;
- struct i2c_parms *p = srdata->i2c_data;
- if (!base)
+ int ret;
+
+ if (index >= ARRAY_SIZE(mxc_i2c_buses)) {
+ debug("Error i2c index\n");
return;
- for (;;) {
- if (!p->base || (p->base == base)) {
- p->base = base;
- if (idle_bus_fn) {
- p->idle_bus_fn = idle_bus_fn;
- p->idle_bus_data = idle_bus_data;
- }
- break;
- }
- p++;
- i++;
- if (i >= ARRAY_SIZE(srdata->i2c_data))
- return;
}
- bus_i2c_set_bus_speed(base, speed);
+
+ mxc_i2c_buses[index].idle_bus_fn = idle_bus_fn;
+ mxc_i2c_buses[index].idle_bus_data = idle_bus_data;
+
+ ret = enable_i2c_clk(1, index);
+ if (ret < 0) {
+ debug("I2C-%d clk fail to enable.\n", index);
+ return;
+ }
+
+ bus_i2c_set_bus_speed(&mxc_i2c_buses[index], speed);
}
/*
@@ -525,13 +595,13 @@ void bus_i2c_init(void *base, int speed, int unused,
*/
static void mxc_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
{
- bus_i2c_init(i2c_get_base(adap), speed, slaveaddr, NULL, NULL);
+ bus_i2c_init(adap->hwadapnr, speed, slaveaddr, NULL, NULL);
}
/*
* Set I2C Speed
*/
-static uint mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
+static u32 mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
{
return bus_i2c_set_bus_speed(i2c_get_base(adap), speed);
}
@@ -556,6 +626,7 @@ U_BOOT_I2C_ADAP_COMPLETE(mxc2, mxc_i2c_init, mxc_i2c_probe,
CONFIG_SYS_MXC_I2C3_SPEED,
CONFIG_SYS_MXC_I2C3_SLAVE, 2)
#endif
+
#ifdef CONFIG_SYS_I2C_MXC_I2C4
U_BOOT_I2C_ADAP_COMPLETE(mxc3, mxc_i2c_init, mxc_i2c_probe,
mxc_i2c_read, mxc_i2c_write,
@@ -563,3 +634,143 @@ U_BOOT_I2C_ADAP_COMPLETE(mxc3, mxc_i2c_init, mxc_i2c_probe,
CONFIG_SYS_MXC_I2C4_SPEED,
CONFIG_SYS_MXC_I2C4_SLAVE, 3)
#endif
+
+#else
+
+static int mxc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
+
+ return bus_i2c_set_bus_speed(i2c_bus, speed);
+}
+
+static int mxc_i2c_probe(struct udevice *bus)
+{
+ struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
+ fdt_addr_t addr;
+ int ret;
+
+ i2c_bus->driver_data = dev_get_driver_data(bus);
+
+ addr = dev_get_addr(bus);
+ if (addr == FDT_ADDR_T_NONE)
+ return -ENODEV;
+
+ i2c_bus->base = addr;
+ i2c_bus->index = bus->seq;
+
+ /* Enable clk */
+ ret = enable_i2c_clk(1, bus->seq);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_idle_bus(i2c_bus);
+ if (ret < 0) {
+ /* Disable clk */
+ enable_i2c_clk(0, bus->seq);
+ return ret;
+ }
+
+ /*
+ * Pinmux settings are in board file now, until pinmux is supported,
+ * we can set pinmux here in probe function.
+ */
+
+ debug("i2c : controller bus %d at %lu , speed %d: ",
+ bus->seq, i2c_bus->base,
+ i2c_bus->speed);
+
+ return 0;
+}
+
+static int mxc_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
+ u32 chip_flags)
+{
+ int ret;
+ struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
+
+ ret = i2c_init_transfer(i2c_bus, chip_addr, 0, 0);
+ if (ret < 0) {
+ debug("%s failed, ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ i2c_imx_stop(i2c_bus);
+
+ return 0;
+}
+
+static int mxc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+ struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
+ int ret = 0;
+ ulong base = i2c_bus->base;
+ int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
+ VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
+
+ /*
+ * Here the 3rd parameter addr and the 4th one alen are set to 0,
+ * because here we only want to send out chip address. The register
+ * address is wrapped in msg.
+ */
+ ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0);
+ if (ret < 0) {
+ debug("i2c_init_transfer error: %d\n", ret);
+ return ret;
+ }
+
+ for (; nmsgs > 0; nmsgs--, msg++) {
+ bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
+ debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
+ if (msg->flags & I2C_M_RD)
+ ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
+ msg->len);
+ else {
+ ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
+ msg->len);
+ if (ret)
+ break;
+ if (next_is_read) {
+ /* Reuse ret */
+ ret = readb(base + (I2CR << reg_shift));
+ ret |= I2CR_RSTA;
+ writeb(ret, base + (I2CR << reg_shift));
+
+ ret = tx_byte(i2c_bus, (msg->addr << 1) | 1);
+ if (ret < 0) {
+ i2c_imx_stop(i2c_bus);
+ break;
+ }
+ }
+ }
+ }
+
+ if (ret)
+ debug("i2c_write: error sending\n");
+
+ i2c_imx_stop(i2c_bus);
+
+ return ret;
+}
+
+static const struct dm_i2c_ops mxc_i2c_ops = {
+ .xfer = mxc_i2c_xfer,
+ .probe_chip = mxc_i2c_probe_chip,
+ .set_bus_speed = mxc_i2c_set_bus_speed,
+};
+
+static const struct udevice_id mxc_i2c_ids[] = {
+ { .compatible = "fsl,imx21-i2c", },
+ { .compatible = "fsl,vf610-i2c", .data = I2C_QUIRK_FLAG, },
+ {}
+};
+
+U_BOOT_DRIVER(i2c_mxc) = {
+ .name = "i2c_mxc",
+ .id = UCLASS_I2C,
+ .of_match = mxc_i2c_ids,
+ .probe = mxc_i2c_probe,
+ .priv_auto_alloc_size = sizeof(struct mxc_i2c_bus),
+ .ops = &mxc_i2c_ops,
+};
+#endif
diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c
index d6adc0f..2c84c41 100644
--- a/drivers/i2c/sandbox_i2c.c
+++ b/drivers/i2c/sandbox_i2c.c
@@ -18,14 +18,15 @@
DECLARE_GLOBAL_DATA_PTR;
-struct dm_sandbox_i2c_emul_priv {
- struct udevice *emul;
+struct sandbox_i2c_priv {
+ bool test_mode;
};
static int get_emul(struct udevice *dev, struct udevice **devp,
struct dm_i2c_ops **opsp)
{
struct dm_i2c_chip *plat;
+ struct udevice *child;
int ret;
*devp = NULL;
@@ -37,9 +38,22 @@ static int get_emul(struct udevice *dev, struct udevice **devp,
if (ret)
return ret;
- ret = device_get_child(dev, 0, &plat->emul);
- if (ret)
- return ret;
+ for (device_find_first_child(dev, &child); child;
+ device_find_next_child(&child)) {
+ if (device_get_uclass_id(child) != UCLASS_I2C_EMUL)
+ continue;
+
+ ret = device_probe(child);
+ if (ret)
+ return ret;
+
+ break;
+ }
+
+ if (child)
+ plat->emul = child;
+ else
+ return -ENODEV;
}
*devp = plat->emul;
*opsp = i2c_get_ops(plat->emul);
@@ -47,17 +61,25 @@ static int get_emul(struct udevice *dev, struct udevice **devp,
return 0;
}
+void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode)
+{
+ struct sandbox_i2c_priv *priv = dev_get_priv(bus);
+
+ priv->test_mode = test_mode;
+}
+
static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
int nmsgs)
{
struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
+ struct sandbox_i2c_priv *priv = dev_get_priv(bus);
struct dm_i2c_ops *ops;
struct udevice *emul, *dev;
bool is_read;
int ret;
/* Special test code to return success but with no emulation */
- if (msg->addr == SANDBOX_I2C_TEST_ADDR)
+ if (priv->test_mode && msg->addr == SANDBOX_I2C_TEST_ADDR)
return 0;
ret = i2c_get_chip(bus, msg->addr, 1, &dev);
@@ -68,13 +90,18 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
if (ret)
return ret;
- /*
- * For testing, don't allow writing above 100KHz for writes and
- * 400KHz for reads
- */
- is_read = nmsgs > 1;
- if (i2c->speed_hz > (is_read ? 400000 : 100000))
- return -EINVAL;
+ if (priv->test_mode) {
+ /*
+ * For testing, don't allow writing above 100KHz for writes and
+ * 400KHz for reads.
+ */
+ is_read = nmsgs > 1;
+ if (i2c->speed_hz > (is_read ? 400000 : 100000)) {
+ debug("%s: Max speed exceeded\n", __func__);
+ return -EINVAL;
+ }
+ }
+
return ops->xfer(emul, msg, nmsgs);
}
@@ -92,4 +119,5 @@ U_BOOT_DRIVER(i2c_sandbox) = {
.id = UCLASS_I2C,
.of_match = sandbox_i2c_ids,
.ops = &sandbox_i2c_ops,
+ .priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv),
};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0e571d9..64b07a3 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -60,3 +60,16 @@ config CONFIG_FSL_SEC_MON
system states.
Security Monitor can be transitioned on any security failures,
like software violations or hardware security violations.
+
+config PCA9551_LED
+ bool "Enable PCA9551 LED driver"
+ help
+ Enable driver for PCA9551 LED controller. This controller
+ is connected via I2C. So I2C needs to be enabled.
+
+config PCA9551_I2C_ADDR
+ hex "I2C address of PCA9551 LED controller"
+ depends on PCA9551_LED
+ default 0x60
+ help
+ The I2C address of the PCA9551 LED controller.
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 25630c3..120babc 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -31,3 +31,4 @@ obj-$(CONFIG_SANDBOX) += swap_case.o
obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
+obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c
index 982bac7..4b6ac6a 100644
--- a/drivers/misc/cros_ec.c
+++ b/drivers/misc/cros_ec.c
@@ -986,7 +986,8 @@ int cros_ec_register(struct udevice *dev)
}
/* Remember this device for use by the cros_ec command */
- debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id);
+ debug("Google Chrome EC v%d CROS-EC driver ready, id '%s'\n",
+ cdev->protocol_version, id);
return 0;
}
diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c
index df41e82..7509612 100644
--- a/drivers/misc/cros_ec_sandbox.c
+++ b/drivers/misc/cros_ec_sandbox.c
@@ -459,6 +459,8 @@ static int process_cmd(struct ec_state *ec,
case EC_CMD_MKBP_STATE:
len = cros_ec_keyscan(ec, resp_data);
break;
+ case EC_CMD_ENTERING_MODE:
+ break;
default:
printf(" ** Unknown EC command %#02x\n", req_hdr->command);
return -1;
diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c
index ac2ee86..0686925 100644
--- a/drivers/misc/cros_ec_spi.c
+++ b/drivers/misc/cros_ec_spi.c
@@ -25,6 +25,8 @@ int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes)
{
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
struct spi_slave *slave = dev_get_parentdata(dev->dev);
+ ulong start;
+ uint8_t byte;
int rv;
/* Do the transfer */
@@ -33,10 +35,25 @@ int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes)
return -1;
}
- rv = spi_xfer(slave, max(out_bytes, in_bytes) * 8,
- dev->dout, dev->din,
- SPI_XFER_BEGIN | SPI_XFER_END);
+ rv = spi_xfer(slave, out_bytes * 8, dev->dout, NULL, SPI_XFER_BEGIN);
+ if (rv)
+ goto done;
+ start = get_timer(0);
+ while (1) {
+ rv = spi_xfer(slave, 8, NULL, &byte, 0);
+ if (byte == SPI_PREAMBLE_END_BYTE)
+ break;
+ if (rv)
+ goto done;
+ if (get_timer(start) > 100) {
+ rv = -ETIMEDOUT;
+ goto done;
+ }
+ }
+ rv = spi_xfer(slave, in_bytes * 8, NULL, dev->din, 0);
+done:
+ spi_xfer(slave, 0, NULL, NULL, SPI_XFER_END);
spi_release_bus(slave);
if (rv) {
diff --git a/drivers/misc/i2c_eeprom_emul.c b/drivers/misc/i2c_eeprom_emul.c
index 7343445..4410d03 100644
--- a/drivers/misc/i2c_eeprom_emul.c
+++ b/drivers/misc/i2c_eeprom_emul.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <dm.h>
+#include <errno.h>
#include <fdtdec.h>
#include <i2c.h>
#include <malloc.h>
diff --git a/drivers/misc/pca9551_led.c b/drivers/misc/pca9551_led.c
new file mode 100644
index 0000000..79b1e20
--- /dev/null
+++ b/drivers/misc/pca9551_led.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 Stefan Roese <sr@denx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <i2c.h>
+
+#ifndef CONFIG_PCA9551_I2C_ADDR
+#error "CONFIG_PCA9551_I2C_ADDR not defined!"
+#endif
+
+#define PCA9551_REG_INPUT 0x00 /* Input register (read only) */
+#define PCA9551_REG_PSC0 0x01 /* Frequency prescaler 0 */
+#define PCA9551_REG_PWM0 0x02 /* PWM0 */
+#define PCA9551_REG_PSC1 0x03 /* Frequency prescaler 1 */
+#define PCA9551_REG_PWM1 0x04 /* PWM1 */
+#define PCA9551_REG_LS0 0x05 /* LED0 to LED3 selector */
+#define PCA9551_REG_LS1 0x06 /* LED4 to LED7 selector */
+
+#define PCA9551_CTRL_AI (1 << 4) /* Auto-increment flag */
+
+#define PCA9551_LED_STATE_ON 0x00
+#define PCA9551_LED_STATE_OFF 0x01
+#define PCA9551_LED_STATE_BLINK0 0x02
+#define PCA9551_LED_STATE_BLINK1 0x03
+
+struct pca9551_blink_rate {
+ u8 psc; /* Frequency preescaler, see PCA9551_7.pdf p. 6 */
+ u8 pwm; /* Pulse width modulation, see PCA9551_7.pdf p. 6 */
+};
+
+static int freq0, freq1;
+
+static int pca9551_led_get_state(int led, int *state)
+{
+ unsigned int reg;
+ u8 shift, buf;
+ int ret;
+
+ if (led < 0 || led > 7) {
+ return -EINVAL;
+ } else if (led < 4) {
+ reg = PCA9551_REG_LS0;
+ shift = led << 1;
+ } else {
+ reg = PCA9551_REG_LS1;
+ shift = (led - 4) << 1;
+ }
+
+ ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
+ if (ret)
+ return ret;
+
+ *state = (buf >> shift) & 0x03;
+ return 0;
+}
+
+static int pca9551_led_set_state(int led, int state)
+{
+ unsigned int reg;
+ u8 shift, buf, mask;
+ int ret;
+
+ if (led < 0 || led > 7) {
+ return -EINVAL;
+ } else if (led < 4) {
+ reg = PCA9551_REG_LS0;
+ shift = led << 1;
+ } else {
+ reg = PCA9551_REG_LS1;
+ shift = (led - 4) << 1;
+ }
+ mask = 0x03 << shift;
+
+ ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
+ if (ret)
+ return ret;
+
+ buf = (buf & ~mask) | ((state & 0x03) << shift);
+
+ ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
+{
+ unsigned int reg;
+ int ret;
+
+ switch (idx) {
+ case 0:
+ reg = PCA9551_REG_PSC0;
+ break;
+ case 1:
+ reg = PCA9551_REG_PSC1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ reg |= PCA9551_CTRL_AI;
+
+ ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Functions referenced by cmd_led.c
+ */
+void __led_set(led_id_t mask, int state)
+{
+ if (state == STATUS_LED_OFF)
+ pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
+ else
+ pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
+}
+
+void __led_toggle(led_id_t mask)
+{
+ int state = 0;
+
+ pca9551_led_get_state(mask, &state);
+ pca9551_led_set_state(mask, !state);
+}
+
+void __led_blink(led_id_t mask, int freq)
+{
+ struct pca9551_blink_rate rate;
+ int mode;
+ int blink;
+
+ if ((freq0 == 0) || (freq == freq0)) {
+ blink = 0;
+ mode = PCA9551_LED_STATE_BLINK0;
+ freq0 = freq;
+ } else {
+ blink = 1;
+ mode = PCA9551_LED_STATE_BLINK1;
+ freq1 = freq;
+ }
+
+ rate.psc = ((freq * 38) / 1000) - 1;
+ rate.pwm = 128; /* 50% duty cycle */
+
+ pca9551_led_set_blink_rate(blink, rate);
+ pca9551_led_set_state(mask, mode);
+}
diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c
index f6028ba..3b8aa48 100644
--- a/drivers/misc/swap_case.c
+++ b/drivers/misc/swap_case.c
@@ -9,6 +9,7 @@
#include <common.h>
#include <dm.h>
+#include <errno.h>
#include <pci.h>
#include <asm/test.h>
#include <linux/ctype.h>
diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c
index d576d31..895604d 100644
--- a/drivers/mtd/spi/sandbox.c
+++ b/drivers/mtd/spi/sandbox.c
@@ -129,7 +129,7 @@ static int sandbox_sf_probe(struct udevice *dev)
}
}
if (cs == -1) {
- printf("Error: Unknown chip select for device '%s'",
+ printf("Error: Unknown chip select for device '%s'\n",
dev->name);
return -EINVAL;
}
diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c
index 4b25902..350e21a 100644
--- a/drivers/mtd/spi/sf-uclass.c
+++ b/drivers/mtd/spi/sf-uclass.c
@@ -53,10 +53,10 @@ int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
{
struct spi_slave *slave;
struct udevice *bus;
- char name[20], *str;
+ char name[30], *str;
int ret;
- snprintf(name, sizeof(name), "%d:%d", busnum, cs);
+ snprintf(name, sizeof(name), "spi_flash@%d:%d", busnum, cs);
str = strdup(name);
ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode,
"spi_flash_std", str, &bus, &slave);
diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c
index e239ff4..4e083d3 100644
--- a/drivers/net/sandbox.c
+++ b/drivers/net/sandbox.c
@@ -11,6 +11,7 @@
#include <dm.h>
#include <malloc.h>
#include <net.h>
+#include <asm/test.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -30,6 +31,7 @@ struct eth_sandbox_priv {
};
static bool disabled[8] = {false};
+static bool skip_timeout;
/*
* sandbox_eth_disable_response()
@@ -42,6 +44,16 @@ void sandbox_eth_disable_response(int index, bool disable)
disabled[index] = disable;
}
+/*
+ * sandbox_eth_skip_timeout()
+ *
+ * When the first packet read is attempted, fast-forward time
+ */
+void sandbox_eth_skip_timeout(void)
+{
+ skip_timeout = true;
+}
+
static int sb_eth_start(struct udevice *dev)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
@@ -144,6 +156,11 @@ static int sb_eth_recv(struct udevice *dev, uchar **packetp)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ if (skip_timeout) {
+ sandbox_timer_add_offset(10000UL);
+ skip_timeout = false;
+ }
+
if (priv->recv_packet_length) {
int lcl_recv_packet_length = priv->recv_packet_length;
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index f8f0239..23cdd71 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,9 @@
+menu "Power"
+
+source "drivers/power/pmic/Kconfig"
+
+source "drivers/power/regulator/Kconfig"
+
config AXP221_POWER
boolean "axp221 / axp223 pmic support"
depends on MACH_SUN6I || MACH_SUN8I
@@ -73,3 +79,5 @@ config AXP221_ELDO3_VOLT
disable eldo3. On some A31(s) tablets it might be used to supply
1.2V for the SSD2828 chip (converter of parallel LCD interface
into MIPI DSI).
+
+endmenu
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 2145652..a2d3c04 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -15,7 +15,6 @@ obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o
obj-$(CONFIG_TWL4030_POWER) += twl4030.o
obj-$(CONFIG_TWL6030_POWER) += twl6030.o
obj-$(CONFIG_PALMAS_POWER) += palmas.o
-
obj-$(CONFIG_POWER) += power_core.o
obj-$(CONFIG_DIALOG_POWER) += power_dialog.o
obj-$(CONFIG_POWER_FSL) += power_fsl.o
diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c
index a60bb5f..c09e1de 100644
--- a/drivers/power/as3722.c
+++ b/drivers/power/as3722.c
@@ -27,7 +27,7 @@
#define AS3722_DEVICE_ID 0x0c
#define AS3722_ASIC_ID2 0x91
-static int as3722_read(struct udevice *pmic, u8 reg, u8 *value)
+int as3722_read(struct udevice *pmic, u8 reg, u8 *value)
{
int err;
@@ -38,7 +38,7 @@ static int as3722_read(struct udevice *pmic, u8 reg, u8 *value)
return 0;
}
-static int as3722_write(struct udevice *pmic, u8 reg, u8 value)
+int as3722_write(struct udevice *pmic, u8 reg, u8 value)
{
int err;
@@ -234,6 +234,15 @@ int as3722_gpio_direction_output(struct udevice *pmic, unsigned int gpio,
return 0;
}
+/* Temporary function until we get the pmic framework */
+int as3722_get(struct udevice **devp)
+{
+ int bus = 0;
+ int address = 0x40;
+
+ return i2c_get_chip_for_busnum(bus, address, 1, devp);
+}
+
int as3722_init(struct udevice **devp)
{
struct udevice *pmic;
@@ -258,7 +267,8 @@ int as3722_init(struct udevice **devp)
debug("AS3722 revision %#x found on I2C bus %u, address %#x\n",
revision, bus, address);
- *devp = pmic;
+ if (devp)
+ *devp = pmic;
return 0;
}
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
new file mode 100644
index 0000000..164f421
--- /dev/null
+++ b/drivers/power/pmic/Kconfig
@@ -0,0 +1,43 @@
+config DM_PMIC
+ bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
+ depends on DM
+ ---help---
+ This config enables the driver-model PMIC support.
+ UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
+ For the multi-function PMIC devices, this can be used as parent I/O
+ device for each IC's interface. Then, each children uses its parent
+ for read/write. For detailed description, please refer to the files:
+ - 'drivers/power/pmic/pmic-uclass.c'
+ - 'include/power/pmic.h'
+
+config DM_PMIC_MAX77686
+ bool "Enable Driver Model for PMIC MAX77686"
+ depends on DM_PMIC
+ ---help---
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX77686. The driver implements read/write operations.
+
+config DM_PMIC_SANDBOX
+ bool "Enable Driver Model for emulated Sandbox PMIC "
+ depends on DM_PMIC
+ ---help---
+ Enable the driver for Sandbox PMIC emulation. The emulated PMIC device
+ depends on two drivers:
+ - sandbox PMIC I/O driver - implements dm pmic operations
+ - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission
+
+ A detailed information can be found in header: '<power/sandbox_pmic.h>'
+
+ The Sandbox PMIC info:
+ * I/O interface:
+ - I2C chip address: 0x40
+ - first register address: 0x0
+ - register count: 0x10
+ * Adjustable outputs:
+ - 2x LDO
+ - 2x BUCK
+ - Each, with a different operating conditions (header).
+ * Reset values:
+ - set by i2c emul driver's probe() (defaults in header)
+
+ Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 985cfdb..ae86f04 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -5,6 +5,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
+obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
+obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
diff --git a/drivers/power/pmic/i2c_pmic_emul.c b/drivers/power/pmic/i2c_pmic_emul.c
new file mode 100644
index 0000000..aeab5c9
--- /dev/null
+++ b/drivers/power/pmic/i2c_pmic_emul.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/sandbox_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct sandbox_i2c_pmic_plat_data - platform data for the PMIC
+ *
+ * @rw_reg: PMICs register of the chip I/O transaction
+ * @reg: PMICs registers array
+ */
+struct sandbox_i2c_pmic_plat_data {
+ u8 rw_reg;
+ u8 reg[SANDBOX_PMIC_REG_COUNT];
+};
+
+static int sandbox_i2c_pmic_read_data(struct udevice *emul, uchar chip,
+ uchar *buffer, int len)
+{
+ struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
+
+ if (plat->rw_reg + len > SANDBOX_PMIC_REG_COUNT) {
+ error("Request exceeds PMIC register range! Max register: %#x",
+ SANDBOX_PMIC_REG_COUNT);
+ return -EFAULT;
+ }
+
+ debug("Read PMIC: %#x at register: %#x count: %d\n",
+ (unsigned)chip & 0xff, plat->rw_reg, len);
+
+ memcpy(buffer, &plat->reg[plat->rw_reg], len);
+
+ return 0;
+}
+
+static int sandbox_i2c_pmic_write_data(struct udevice *emul, uchar chip,
+ uchar *buffer, int len,
+ bool next_is_read)
+{
+ struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
+
+ /* Probe only */
+ if (!len)
+ return 0;
+
+ /* Set PMIC register for I/O */
+ plat->rw_reg = *buffer;
+
+ debug("Write PMIC: %#x at register: %#x count: %d\n",
+ (unsigned)chip & 0xff, plat->rw_reg, len);
+
+ /* For read operation, set (write) only chip reg */
+ if (next_is_read)
+ return 0;
+
+ buffer++;
+ len--;
+
+ if (plat->rw_reg + len > SANDBOX_PMIC_REG_COUNT) {
+ error("Request exceeds PMIC register range! Max register: %#x",
+ SANDBOX_PMIC_REG_COUNT);
+ }
+
+ memcpy(&plat->reg[plat->rw_reg], buffer, len);
+
+ return 0;
+}
+
+static int sandbox_i2c_pmic_xfer(struct udevice *emul, struct i2c_msg *msg,
+ int nmsgs)
+{
+ int ret = 0;
+
+ for (; nmsgs > 0; nmsgs--, msg++) {
+ bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
+ if (msg->flags & I2C_M_RD) {
+ ret = sandbox_i2c_pmic_read_data(emul, msg->addr,
+ msg->buf, msg->len);
+ } else {
+ ret = sandbox_i2c_pmic_write_data(emul, msg->addr,
+ msg->buf, msg->len,
+ next_is_read);
+ }
+
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int sandbox_i2c_pmic_ofdata_to_platdata(struct udevice *emul)
+{
+ struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
+ const u8 *reg_defaults;
+
+ debug("%s:%d Setting PMIC default registers\n", __func__, __LINE__);
+
+ reg_defaults = fdtdec_locate_byte_array(gd->fdt_blob, emul->of_offset,
+ "reg-defaults",
+ SANDBOX_PMIC_REG_COUNT);
+
+ if (!reg_defaults) {
+ error("Property \"reg-defaults\" not found for device: %s!",
+ emul->name);
+ return -EINVAL;
+ }
+
+ memcpy(&plat->reg, reg_defaults, SANDBOX_PMIC_REG_COUNT);
+
+ return 0;
+}
+
+struct dm_i2c_ops sandbox_i2c_pmic_emul_ops = {
+ .xfer = sandbox_i2c_pmic_xfer,
+};
+
+static const struct udevice_id sandbox_i2c_pmic_ids[] = {
+ { .compatible = "sandbox,i2c-pmic" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_i2c_pmic_emul) = {
+ .name = "sandbox_i2c_pmic_emul",
+ .id = UCLASS_I2C_EMUL,
+ .of_match = sandbox_i2c_pmic_ids,
+ .ofdata_to_platdata = sandbox_i2c_pmic_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_pmic_plat_data),
+ .ops = &sandbox_i2c_pmic_emul_ops,
+};
diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c
new file mode 100644
index 0000000..3523b4a
--- /dev/null
+++ b/drivers/power/pmic/max77686.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max77686_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_children_info[] = {
+ { .prefix = "ldo", .driver = MAX77686_LDO_DRIVER },
+ { .prefix = "buck", .driver = MAX77686_BUCK_DRIVER },
+ { },
+};
+
+static int max77686_reg_count(struct udevice *dev)
+{
+ return MAX77686_NUM_OF_REGS;
+}
+
+static int max77686_write(struct udevice *dev, uint reg, const uint8_t *buff,
+ int len)
+{
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ error("write error to device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int max77686_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ if (dm_i2c_read(dev, reg, buff, len)) {
+ error("read error from device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int max77686_bind(struct udevice *dev)
+{
+ int regulators_node;
+ const void *blob = gd->fdt_blob;
+ int children;
+
+ regulators_node = fdt_subnode_offset(blob, dev->of_offset,
+ "voltage-regulators");
+ if (regulators_node <= 0) {
+ debug("%s: %s regulators subnode not found!", __func__,
+ dev->name);
+ return -ENXIO;
+ }
+
+ debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+ if (!children)
+ debug("%s: %s - no child found\n", __func__, dev->name);
+
+ /* Always return success for this device */
+ return 0;
+}
+
+static struct dm_pmic_ops max77686_ops = {
+ .reg_count = max77686_reg_count,
+ .read = max77686_read,
+ .write = max77686_write,
+};
+
+static const struct udevice_id max77686_ids[] = {
+ { .compatible = "maxim,max77686" },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_max77686) = {
+ .name = "max77686 pmic",
+ .id = UCLASS_PMIC,
+ .of_match = max77686_ids,
+ .bind = max77686_bind,
+ .ops = &max77686_ops,
+};
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c
new file mode 100644
index 0000000..812ac13
--- /dev/null
+++ b/drivers/power/pmic/pmic-uclass.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+#include <linux/ctype.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static ulong str_get_num(const char *ptr, const char *maxptr)
+{
+ if (!ptr || !maxptr)
+ return 0;
+
+ while (!isdigit(*ptr) && ptr++ < maxptr);
+
+ return simple_strtoul(ptr, NULL, 0);
+}
+
+int pmic_bind_children(struct udevice *pmic, int offset,
+ const struct pmic_child_info *child_info)
+{
+ const struct pmic_child_info *info;
+ const void *blob = gd->fdt_blob;
+ struct driver *drv;
+ struct udevice *child;
+ const char *node_name;
+ int node_name_len;
+ int bind_count = 0;
+ int node;
+ int prefix_len;
+ int ret;
+
+ debug("%s for '%s' at node offset: %d\n", __func__, pmic->name,
+ pmic->of_offset);
+
+ for (node = fdt_first_subnode(blob, offset);
+ node > 0;
+ node = fdt_next_subnode(blob, node)) {
+ node_name = fdt_get_name(blob, node, &node_name_len);
+
+ debug("* Found child node: '%s' at offset:%d\n", node_name,
+ node);
+
+ child = NULL;
+ for (info = child_info; info->prefix && info->driver; info++) {
+ prefix_len = strlen(info->prefix);
+ if (strncasecmp(info->prefix, node_name, prefix_len))
+ continue;
+
+ debug(" - compatible prefix: '%s'\n", info->prefix);
+
+ drv = lists_driver_lookup_name(info->driver);
+ if (!drv) {
+ debug(" - driver: '%s' not found!\n",
+ info->driver);
+ continue;
+ }
+
+ debug(" - found child driver: '%s'\n", drv->name);
+
+ ret = device_bind(pmic, drv, node_name, NULL,
+ node, &child);
+ if (ret) {
+ debug(" - child binding error: %d\n", ret);
+ continue;
+ }
+
+ debug(" - bound child device: '%s'\n", child->name);
+
+ child->driver_data = str_get_num(node_name +
+ prefix_len,
+ node_name +
+ node_name_len);
+
+ debug(" - set 'child->driver_data': %lu\n",
+ child->driver_data);
+ break;
+ }
+
+ if (child)
+ bind_count++;
+ else
+ debug(" - compatible prefix not found\n");
+ }
+
+ debug("Bound: %d childs for PMIC: '%s'\n", bind_count, pmic->name);
+ return bind_count;
+}
+
+int pmic_get(const char *name, struct udevice **devp)
+{
+ return uclass_get_device_by_name(UCLASS_PMIC, name, devp);
+}
+
+int pmic_reg_count(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->reg_count)
+ return -ENOSYS;
+
+ return ops->reg_count(dev);
+}
+
+int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len)
+{
+ const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
+
+ if (!buffer)
+ return -EFAULT;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ return ops->read(dev, reg, buffer, len);
+}
+
+int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len)
+{
+ const struct dm_pmic_ops *ops = dev_get_driver_ops(dev);
+
+ if (!buffer)
+ return -EFAULT;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ return ops->write(dev, reg, buffer, len);
+}
+
+UCLASS_DRIVER(pmic) = {
+ .id = UCLASS_PMIC,
+ .name = "pmic",
+};
diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c
index 95b1a57..1ad810a 100644
--- a/drivers/power/pmic/pmic_max77686.c
+++ b/drivers/power/pmic/pmic_max77686.c
@@ -295,7 +295,7 @@ int pmic_init(unsigned char bus)
p->name = name;
p->interface = PMIC_I2C;
- p->number_of_regs = PMIC_NUM_OF_REGS;
+ p->number_of_regs = MAX77686_NUM_OF_REGS;
p->hw.i2c.tx_num = 1;
puts("Board PMIC init\n");
diff --git a/drivers/power/pmic/sandbox.c b/drivers/power/pmic/sandbox.c
new file mode 100644
index 0000000..3e56acd
--- /dev/null
+++ b/drivers/power/pmic/sandbox.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/sandbox_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_children_info[] = {
+ { .prefix = SANDBOX_OF_LDO_PREFIX, .driver = SANDBOX_LDO_DRIVER },
+ { .prefix = SANDBOX_OF_BUCK_PREFIX, .driver = SANDBOX_BUCK_DRIVER },
+ { },
+};
+
+static int sandbox_pmic_reg_count(struct udevice *dev)
+{
+ return SANDBOX_PMIC_REG_COUNT;
+}
+
+static int sandbox_pmic_write(struct udevice *dev, uint reg,
+ const uint8_t *buff, int len)
+{
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ error("write error to device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sandbox_pmic_read(struct udevice *dev, uint reg,
+ uint8_t *buff, int len)
+{
+ if (dm_i2c_read(dev, reg, buff, len)) {
+ error("read error from device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sandbox_pmic_bind(struct udevice *dev)
+{
+ if (!pmic_bind_children(dev, dev->of_offset, pmic_children_info))
+ error("%s:%d PMIC: %s - no child found!", __func__, __LINE__,
+ dev->name);
+
+ /* Always return success for this device - allows for PMIC I/O */
+ return 0;
+}
+
+static struct dm_pmic_ops sandbox_pmic_ops = {
+ .reg_count = sandbox_pmic_reg_count,
+ .read = sandbox_pmic_read,
+ .write = sandbox_pmic_write,
+};
+
+static const struct udevice_id sandbox_pmic_ids[] = {
+ { .compatible = "sandbox,pmic" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_pmic) = {
+ .name = "sandbox_pmic",
+ .id = UCLASS_PMIC,
+ .of_match = sandbox_pmic_ids,
+ .bind = sandbox_pmic_bind,
+ .ops = &sandbox_pmic_ops,
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
new file mode 100644
index 0000000..6289b83
--- /dev/null
+++ b/drivers/power/regulator/Kconfig
@@ -0,0 +1,63 @@
+config DM_REGULATOR
+ bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
+ depends on DM
+ ---help---
+ This config enables the driver model regulator support.
+ UCLASS_REGULATOR - designed to provide a common API for basic regulator's
+ functions, like get/set Voltage or Current value, enable state, etc...
+ Note:
+ When enabling this, please read the description, found in the files:
+ - 'include/power/pmic.h'
+ - 'include/power/regulator.h'
+ - 'drivers/power/pmic/pmic-uclass.c'
+ - 'drivers/power/pmic/regulator-uclass.c'
+ It's important to call the device_bind() with the proper node offset,
+ when binding the regulator devices. The pmic_bind_childs() can be used
+ for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
+ otherwise. Detailed information can be found in the header file.
+
+config DM_REGULATOR_MAX77686
+ bool "Enable Driver Model for REGULATOR MAX77686"
+ depends on DM_REGULATOR && DM_PMIC_MAX77686
+ ---help---
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR MAX77686. The driver implements get/set api for:
+ value, enable and mode.
+
+config DM_REGULATOR_FIXED
+ bool "Enable Driver Model for REGULATOR Fixed value"
+ depends on DM_REGULATOR
+ ---help---
+ This config enables implementation of driver-model regulator uclass
+ features for fixed value regulators. The driver implements get/set api
+ for enable and get only for voltage value.
+
+config DM_REGULATOR_SANDBOX
+ bool "Enable Driver Model for Sandbox PMIC regulator"
+ depends on DM_REGULATOR && DM_PMIC_SANDBOX
+ ---help---
+ Enable the regulator driver for emulated Sandbox PMIC.
+ The emulated PMIC device depends on two drivers:
+ - sandbox PMIC I/O driver - implements dm pmic operations
+ - sandbox PMIC regulator driver - implements dm regulator operations
+ - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission
+
+ The regulator driver provides uclass operations for sandbox PMIC's
+ regulators. The driver implements get/set api for: voltage, current,
+ operation mode and enable state.
+ The driver supports LDO and BUCK regulators.
+
+ The Sandbox PMIC info:
+ * I/O interface:
+ - I2C chip address: 0x40
+ - first register address: 0x0
+ - register count: 0x10
+ * Adjustable outputs:
+ - 2x LDO
+ - 2x BUCK
+ - Each, with a different operating conditions (header).
+ * Reset values:
+ - set by i2c emul driver's probe() (defaults in header)
+
+ A detailed information can be found in header: '<power/sandbox_pmic.h>'
+ Binding info: 'doc/device-tree-bindings/pmic/max77686.txt'
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
new file mode 100644
index 0000000..96aa624
--- /dev/null
+++ b/drivers/power/regulator/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright (C) 2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
+obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
+obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
+obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
diff --git a/drivers/power/regulator/fixed.c b/drivers/power/regulator/fixed.c
new file mode 100644
index 0000000..d053817
--- /dev/null
+++ b/drivers/power/regulator/fixed.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct fixed_regulator_platdata {
+ struct gpio_desc gpio; /* GPIO for regulator enable control */
+};
+
+static int fixed_regulator_ofdata_to_platdata(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ struct fixed_regulator_platdata *dev_pdata;
+ struct gpio_desc *gpio;
+ int ret;
+
+ dev_pdata = dev_get_platdata(dev);
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata)
+ return -ENXIO;
+
+ /* Set type to fixed */
+ uc_pdata->type = REGULATOR_TYPE_FIXED;
+
+ /* Get fixed regulator gpio desc */
+ gpio = &dev_pdata->gpio;
+ ret = gpio_request_by_name(dev, "gpio", 0, gpio, GPIOD_IS_OUT);
+ if (ret)
+ debug("Fixed regulator gpio - not found! Error: %d", ret);
+
+ return 0;
+}
+
+static int fixed_regulator_get_value(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata)
+ return -ENXIO;
+
+ if (uc_pdata->min_uV != uc_pdata->max_uV) {
+ debug("Invalid constraints for: %s\n", uc_pdata->name);
+ return -EINVAL;
+ }
+
+ return uc_pdata->min_uV;
+}
+
+static int fixed_regulator_get_current(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata)
+ return -ENXIO;
+
+ if (uc_pdata->min_uA != uc_pdata->max_uA) {
+ debug("Invalid constraints for: %s\n", uc_pdata->name);
+ return -EINVAL;
+ }
+
+ return uc_pdata->min_uA;
+}
+
+static bool fixed_regulator_get_enable(struct udevice *dev)
+{
+ struct fixed_regulator_platdata *dev_pdata = dev_get_platdata(dev);
+
+ if (!dev_pdata->gpio.dev)
+ return false;
+
+ return dm_gpio_get_value(&dev_pdata->gpio);
+}
+
+static int fixed_regulator_set_enable(struct udevice *dev, bool enable)
+{
+ struct fixed_regulator_platdata *dev_pdata = dev_get_platdata(dev);
+ int ret;
+
+ if (!dev_pdata->gpio.dev)
+ return -ENOSYS;
+
+ ret = dm_gpio_set_value(&dev_pdata->gpio, enable);
+ if (ret) {
+ error("Can't set regulator : %s gpio to: %d\n", dev->name,
+ enable);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dm_regulator_ops fixed_regulator_ops = {
+ .get_value = fixed_regulator_get_value,
+ .get_current = fixed_regulator_get_current,
+ .get_enable = fixed_regulator_get_enable,
+ .set_enable = fixed_regulator_set_enable,
+};
+
+static const struct udevice_id fixed_regulator_ids[] = {
+ { .compatible = "regulator-fixed" },
+ { },
+};
+
+U_BOOT_DRIVER(fixed_regulator) = {
+ .name = "fixed regulator",
+ .id = UCLASS_REGULATOR,
+ .ops = &fixed_regulator_ops,
+ .of_match = fixed_regulator_ids,
+ .ofdata_to_platdata = fixed_regulator_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct fixed_regulator_platdata),
+};
diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c
new file mode 100644
index 0000000..37ebe94
--- /dev/null
+++ b/drivers/power/regulator/max77686.c
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2012-2015 Samsung Electronics
+ *
+ * Rajeshwari Shinde <rajeshwari.s@samsung.com>
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/max77686_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define MODE(_id, _val, _name) { \
+ .id = _id, \
+ .register_value = _val, \
+ .name = _name, \
+}
+
+/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */
+static struct dm_regulator_mode max77686_ldo_mode_standby1[] = {
+ MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+ MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"),
+ MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+ MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* LDO: 2,6,7,8,10,11,12,14,15,16 */
+static struct dm_regulator_mode max77686_ldo_mode_standby2[] = {
+ MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"),
+ MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"),
+ MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"),
+ MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"),
+};
+
+/* Buck: 1 */
+static struct dm_regulator_mode max77686_buck_mode_standby[] = {
+ MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+ MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+ MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 2,3,4 */
+static struct dm_regulator_mode max77686_buck_mode_lpm[] = {
+ MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+ MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"),
+ MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"),
+ MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+/* Buck: 5,6,7,8,9 */
+static struct dm_regulator_mode max77686_buck_mode_onoff[] = {
+ MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"),
+ MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
+};
+
+static const char max77686_buck_addr[] = {
+ 0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
+};
+
+static int max77686_buck_volt2hex(int buck, int uV)
+{
+ unsigned int hex = 0;
+ unsigned int hex_max = 0;
+
+ switch (buck) {
+ case 2:
+ case 3:
+ case 4:
+ /* hex = (uV - 600000) / 12500; */
+ hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
+ hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+ /**
+ * Those use voltage scaller - temporary not implemented
+ * so return just 0
+ */
+ return -ENOSYS;
+ default:
+ /* hex = (uV - 750000) / 50000; */
+ hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
+ hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+ break;
+ }
+
+ if (hex >= 0 && hex <= hex_max)
+ return hex;
+
+ error("Value: %d uV is wrong for BUCK%d", uV, buck);
+ return -EINVAL;
+}
+
+static int max77686_buck_hex2volt(int buck, int hex)
+{
+ unsigned uV = 0;
+ unsigned int hex_max = 0;
+
+ if (hex < 0)
+ goto bad_hex;
+
+ switch (buck) {
+ case 2:
+ case 3:
+ case 4:
+ hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
+ if (hex > hex_max)
+ goto bad_hex;
+
+ /* uV = hex * 12500 + 600000; */
+ uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN;
+ break;
+ default:
+ hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
+ if (hex > hex_max)
+ goto bad_hex;
+
+ /* uV = hex * 50000 + 750000; */
+ uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN;
+ break;
+ }
+
+ return uV;
+
+bad_hex:
+ error("Value: %#x is wrong for BUCK%d", hex, buck);
+ return -EINVAL;
+}
+
+static int max77686_ldo_volt2hex(int ldo, int uV)
+{
+ unsigned int hex = 0;
+
+ switch (ldo) {
+ case 1:
+ case 2:
+ case 6:
+ case 7:
+ case 8:
+ case 15:
+ hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP;
+ /* hex = (uV - 800000) / 25000; */
+ break;
+ default:
+ hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP;
+ /* hex = (uV - 800000) / 50000; */
+ }
+
+ if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX)
+ return hex;
+
+ error("Value: %d uV is wrong for LDO%d", uV, ldo);
+ return -EINVAL;
+}
+
+static int max77686_ldo_hex2volt(int ldo, int hex)
+{
+ unsigned int uV = 0;
+
+ if (hex > MAX77686_LDO_VOLT_MAX_HEX)
+ goto bad_hex;
+
+ switch (ldo) {
+ case 1:
+ case 2:
+ case 6:
+ case 7:
+ case 8:
+ case 15:
+ /* uV = hex * 25000 + 800000; */
+ uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN;
+ break;
+ default:
+ /* uV = hex * 50000 + 800000; */
+ uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN;
+ }
+
+ return uV;
+
+bad_hex:
+ error("Value: %#x is wrong for ldo%d", hex, ldo);
+ return -EINVAL;
+}
+
+static int max77686_ldo_hex2mode(int ldo, int hex)
+{
+ if (hex > MAX77686_LDO_MODE_MASK)
+ return -EINVAL;
+
+ switch (hex) {
+ case MAX77686_LDO_MODE_OFF:
+ return OPMODE_OFF;
+ case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */
+ /* The same mode values but different meaning for each ldo */
+ switch (ldo) {
+ case 2:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ return OPMODE_STANDBY;
+ default:
+ return OPMODE_LPM;
+ }
+ case MAX77686_LDO_MODE_STANDBY_LPM:
+ return OPMODE_STANDBY_LPM;
+ case MAX77686_LDO_MODE_ON:
+ return OPMODE_ON;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max77686_buck_hex2mode(int buck, int hex)
+{
+ if (hex > MAX77686_BUCK_MODE_MASK)
+ return -EINVAL;
+
+ switch (hex) {
+ case MAX77686_BUCK_MODE_OFF:
+ return OPMODE_OFF;
+ case MAX77686_BUCK_MODE_ON:
+ return OPMODE_ON;
+ case MAX77686_BUCK_MODE_STANDBY:
+ switch (buck) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return OPMODE_STANDBY;
+ default:
+ return -EINVAL;
+ }
+ case MAX77686_BUCK_MODE_LPM:
+ switch (buck) {
+ case 2:
+ case 3:
+ case 4:
+ return OPMODE_LPM;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max77686_buck_modes(int buck, struct dm_regulator_mode **modesp)
+{
+ int ret = -EINVAL;
+
+ if (buck < 1 || buck > MAX77686_BUCK_NUM)
+ return ret;
+
+ switch (buck) {
+ case 1:
+ *modesp = max77686_buck_mode_standby;
+ ret = ARRAY_SIZE(max77686_buck_mode_standby);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ *modesp = max77686_buck_mode_lpm;
+ ret = ARRAY_SIZE(max77686_buck_mode_lpm);
+ break;
+ default:
+ *modesp = max77686_buck_mode_onoff;
+ ret = ARRAY_SIZE(max77686_buck_mode_onoff);
+ }
+
+ return ret;
+}
+
+static int max77686_ldo_modes(int ldo, struct dm_regulator_mode **modesp,
+ struct udevice *dev)
+{
+ int ret = -EINVAL;
+
+ if (ldo < 1 || ldo > MAX77686_LDO_NUM)
+ return ret;
+
+ switch (ldo) {
+ case 2:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ *modesp = max77686_ldo_mode_standby2;
+ ret = ARRAY_SIZE(max77686_ldo_mode_standby2);
+ break;
+ default:
+ *modesp = max77686_ldo_mode_standby1;
+ ret = ARRAY_SIZE(max77686_ldo_mode_standby1);
+ }
+
+ return ret;
+}
+
+static int max77686_ldo_val(struct udevice *dev, int op, int *uV)
+{
+ unsigned int ret, hex, adr;
+ unsigned char val;
+ int ldo;
+
+ if (op == PMIC_OP_GET)
+ *uV = 0;
+
+ ldo = dev->driver_data;
+ if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
+ error("Wrong ldo number: %d", ldo);
+ return -EINVAL;
+ }
+
+ adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+ ret = pmic_read(dev->parent, adr, &val, 1);
+ if (ret)
+ return ret;
+
+ if (op == PMIC_OP_GET) {
+ val &= MAX77686_LDO_VOLT_MASK;
+ ret = max77686_ldo_hex2volt(ldo, val);
+ if (ret < 0)
+ return ret;
+ *uV = ret;
+ return 0;
+ }
+
+ hex = max77686_ldo_volt2hex(ldo, *uV);
+ if (hex < 0)
+ return hex;
+
+ val &= ~MAX77686_LDO_VOLT_MASK;
+ val |= hex;
+ ret = pmic_write(dev->parent, adr, &val, 1);
+
+ return ret;
+}
+
+static int max77686_buck_val(struct udevice *dev, int op, int *uV)
+{
+ unsigned int hex, ret, mask, adr;
+ unsigned char val;
+ int buck;
+
+ buck = dev->driver_data;
+ if (buck < 1 || buck > MAX77686_BUCK_NUM) {
+ error("Wrong buck number: %d", buck);
+ return -EINVAL;
+ }
+
+ if (op == PMIC_OP_GET)
+ *uV = 0;
+
+ /* &buck_out = ctrl + 1 */
+ adr = max77686_buck_addr[buck] + 1;
+
+ /* mask */
+ switch (buck) {
+ case 2:
+ case 3:
+ case 4:
+ /* Those use voltage scallers - will support in the future */
+ mask = MAX77686_BUCK234_VOLT_MASK;
+ return -ENOSYS;
+ default:
+ mask = MAX77686_BUCK_VOLT_MASK;
+ }
+
+ ret = pmic_read(dev->parent, adr, &val, 1);
+ if (ret)
+ return ret;
+
+ if (op == PMIC_OP_GET) {
+ val &= mask;
+ ret = max77686_buck_hex2volt(buck, val);
+ if (ret < 0)
+ return ret;
+ *uV = ret;
+ return 0;
+ }
+
+ hex = max77686_buck_volt2hex(buck, *uV);
+ if (hex < 0)
+ return hex;
+
+ val &= ~mask;
+ val |= hex;
+ ret = pmic_write(dev->parent, adr, &val, 1);
+
+ return ret;
+}
+
+static int max77686_ldo_mode(struct udevice *dev, int op, int *opmode)
+{
+ unsigned int ret, adr, mode;
+ unsigned char val;
+ int ldo;
+
+ if (op == PMIC_OP_GET)
+ *opmode = -EINVAL;
+
+ ldo = dev->driver_data;
+ if (ldo < 1 || ldo > MAX77686_LDO_NUM) {
+ error("Wrong ldo number: %d", ldo);
+ return -EINVAL;
+ }
+
+ adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1;
+
+ ret = pmic_read(dev->parent, adr, &val, 1);
+ if (ret)
+ return ret;
+
+ if (op == PMIC_OP_GET) {
+ val &= MAX77686_LDO_MODE_MASK;
+ ret = max77686_ldo_hex2mode(ldo, val);
+ if (ret < 0)
+ return ret;
+ *opmode = ret;
+ return 0;
+ }
+
+ /* mode */
+ switch (*opmode) {
+ case OPMODE_OFF:
+ mode = MAX77686_LDO_MODE_OFF;
+ break;
+ case OPMODE_LPM:
+ switch (ldo) {
+ case 2:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ return -EINVAL;
+ default:
+ mode = MAX77686_LDO_MODE_LPM;
+ }
+ break;
+ case OPMODE_STANDBY:
+ switch (ldo) {
+ case 2:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ mode = MAX77686_LDO_MODE_STANDBY;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case OPMODE_STANDBY_LPM:
+ mode = MAX77686_LDO_MODE_STANDBY_LPM;
+ break;
+ case OPMODE_ON:
+ mode = MAX77686_LDO_MODE_ON;
+ break;
+ default:
+ mode = 0xff;
+ }
+
+ if (mode == 0xff) {
+ error("Wrong mode: %d for ldo%d", *opmode, ldo);
+ return -EINVAL;
+ }
+
+ val &= ~MAX77686_LDO_MODE_MASK;
+ val |= mode;
+ ret = pmic_write(dev->parent, adr, &val, 1);
+
+ return ret;
+}
+
+static int max77686_ldo_enable(struct udevice *dev, int op, bool *enable)
+{
+ int ret, on_off;
+
+ if (op == PMIC_OP_GET) {
+ ret = max77686_ldo_mode(dev, op, &on_off);
+ if (ret)
+ return ret;
+
+ switch (on_off) {
+ case OPMODE_OFF:
+ *enable = 0;
+ break;
+ case OPMODE_ON:
+ *enable = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (op == PMIC_OP_SET) {
+ switch (*enable) {
+ case 0:
+ on_off = OPMODE_OFF;
+ break;
+ case 1:
+ on_off = OPMODE_ON;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = max77686_ldo_mode(dev, op, &on_off);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77686_buck_mode(struct udevice *dev, int op, int *opmode)
+{
+ unsigned int ret, mask, adr, mode, mode_shift;
+ unsigned char val;
+ int buck;
+
+ buck = dev->driver_data;
+ if (buck < 1 || buck > MAX77686_BUCK_NUM) {
+ error("Wrong buck number: %d", buck);
+ return -EINVAL;
+ }
+
+ adr = max77686_buck_addr[buck];
+
+ /* mask */
+ switch (buck) {
+ case 2:
+ case 3:
+ case 4:
+ mode_shift = MAX77686_BUCK_MODE_SHIFT_2;
+ break;
+ default:
+ mode_shift = MAX77686_BUCK_MODE_SHIFT_1;
+ }
+
+ mask = MAX77686_BUCK_MODE_MASK << mode_shift;
+
+ ret = pmic_read(dev->parent, adr, &val, 1);
+ if (ret)
+ return ret;
+
+ if (op == PMIC_OP_GET) {
+ val &= mask;
+ val >>= mode_shift;
+ ret = max77686_buck_hex2mode(buck, val);
+ if (ret < 0)
+ return ret;
+ *opmode = ret;
+ return 0;
+ }
+
+ /* mode */
+ switch (*opmode) {
+ case OPMODE_OFF:
+ mode = MAX77686_BUCK_MODE_OFF;
+ break;
+ case OPMODE_STANDBY:
+ switch (buck) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ mode = MAX77686_BUCK_MODE_STANDBY << mode_shift;
+ break;
+ default:
+ mode = 0xff;
+ }
+ break;
+ case OPMODE_LPM:
+ switch (buck) {
+ case 2:
+ case 3:
+ case 4:
+ mode = MAX77686_BUCK_MODE_LPM << mode_shift;
+ break;
+ default:
+ mode = 0xff;
+ }
+ break;
+ case OPMODE_ON:
+ mode = MAX77686_BUCK_MODE_ON << mode_shift;
+ break;
+ default:
+ mode = 0xff;
+ }
+
+ if (mode == 0xff) {
+ error("Wrong mode: %d for buck: %d\n", *opmode, buck);
+ return -EINVAL;
+ }
+
+ val &= ~mask;
+ val |= mode;
+ ret = pmic_write(dev->parent, adr, &val, 1);
+
+ return ret;
+}
+
+static int max77686_buck_enable(struct udevice *dev, int op, bool *enable)
+{
+ int ret, on_off;
+
+ if (op == PMIC_OP_GET) {
+ ret = max77686_buck_mode(dev, op, &on_off);
+ if (ret)
+ return ret;
+
+ switch (on_off) {
+ case OPMODE_OFF:
+ *enable = false;
+ break;
+ case OPMODE_ON:
+ *enable = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (op == PMIC_OP_SET) {
+ switch (*enable) {
+ case 0:
+ on_off = OPMODE_OFF;
+ break;
+ case 1:
+ on_off = OPMODE_ON;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = max77686_buck_mode(dev, op, &on_off);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max77686_ldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->mode_count = max77686_ldo_modes(dev->driver_data,
+ &uc_pdata->mode, dev);
+
+ return 0;
+}
+
+static int ldo_get_value(struct udevice *dev)
+{
+ int uV;
+ int ret;
+
+ ret = max77686_ldo_val(dev, PMIC_OP_GET, &uV);
+ if (ret)
+ return ret;
+
+ return uV;
+}
+
+static int ldo_set_value(struct udevice *dev, int uV)
+{
+ return max77686_ldo_val(dev, PMIC_OP_SET, &uV);
+}
+
+static bool ldo_get_enable(struct udevice *dev)
+{
+ bool enable = false;
+ int ret;
+
+ ret = max77686_ldo_enable(dev, PMIC_OP_GET, &enable);
+ if (ret)
+ return ret;
+
+ return enable;
+}
+
+static int ldo_set_enable(struct udevice *dev, bool enable)
+{
+ return max77686_ldo_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static int ldo_get_mode(struct udevice *dev)
+{
+ int mode;
+ int ret;
+
+ ret = max77686_ldo_mode(dev, PMIC_OP_GET, &mode);
+ if (ret)
+ return ret;
+
+ return mode;
+}
+
+static int ldo_set_mode(struct udevice *dev, int mode)
+{
+ return max77686_ldo_mode(dev, PMIC_OP_SET, &mode);
+}
+
+static int max77686_buck_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_BUCK;
+ uc_pdata->mode_count = max77686_buck_modes(dev->driver_data,
+ &uc_pdata->mode);
+
+ return 0;
+}
+
+static int buck_get_value(struct udevice *dev)
+{
+ int uV;
+ int ret;
+
+ ret = max77686_buck_val(dev, PMIC_OP_GET, &uV);
+ if (ret)
+ return ret;
+
+ return uV;
+}
+
+static int buck_set_value(struct udevice *dev, int uV)
+{
+ return max77686_buck_val(dev, PMIC_OP_SET, &uV);
+}
+
+static bool buck_get_enable(struct udevice *dev)
+{
+ bool enable = false;
+ int ret;
+
+ ret = max77686_buck_enable(dev, PMIC_OP_GET, &enable);
+ if (ret)
+ return ret;
+
+ return enable;
+}
+
+static int buck_set_enable(struct udevice *dev, bool enable)
+{
+ return max77686_buck_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static int buck_get_mode(struct udevice *dev)
+{
+ int mode;
+ int ret;
+
+ ret = max77686_buck_mode(dev, PMIC_OP_GET, &mode);
+ if (ret)
+ return ret;
+
+ return mode;
+}
+
+static int buck_set_mode(struct udevice *dev, int mode)
+{
+ return max77686_buck_mode(dev, PMIC_OP_SET, &mode);
+}
+
+static const struct dm_regulator_ops max77686_ldo_ops = {
+ .get_value = ldo_get_value,
+ .set_value = ldo_set_value,
+ .get_enable = ldo_get_enable,
+ .set_enable = ldo_set_enable,
+ .get_mode = ldo_get_mode,
+ .set_mode = ldo_set_mode,
+};
+
+U_BOOT_DRIVER(max77686_ldo) = {
+ .name = MAX77686_LDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &max77686_ldo_ops,
+ .probe = max77686_ldo_probe,
+};
+
+static const struct dm_regulator_ops max77686_buck_ops = {
+ .get_value = buck_get_value,
+ .set_value = buck_set_value,
+ .get_enable = buck_get_enable,
+ .set_enable = buck_set_enable,
+ .get_mode = buck_get_mode,
+ .set_mode = buck_set_mode,
+};
+
+U_BOOT_DRIVER(max77686_buck) = {
+ .name = MAX77686_BUCK_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &max77686_buck_ops,
+ .probe = max77686_buck_probe,
+};
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
new file mode 100644
index 0000000..31ffd44
--- /dev/null
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2014-2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ *modep = NULL;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata)
+ return -ENXIO;
+
+ *modep = uc_pdata->mode;
+ return uc_pdata->mode_count;
+}
+
+int regulator_get_value(struct udevice *dev)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->get_value)
+ return -ENOSYS;
+
+ return ops->get_value(dev);
+}
+
+int regulator_set_value(struct udevice *dev, int uV)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->set_value)
+ return -ENOSYS;
+
+ return ops->set_value(dev, uV);
+}
+
+int regulator_get_current(struct udevice *dev)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->get_current)
+ return -ENOSYS;
+
+ return ops->get_current(dev);
+}
+
+int regulator_set_current(struct udevice *dev, int uA)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->set_current)
+ return -ENOSYS;
+
+ return ops->set_current(dev, uA);
+}
+
+bool regulator_get_enable(struct udevice *dev)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->get_enable)
+ return -ENOSYS;
+
+ return ops->get_enable(dev);
+}
+
+int regulator_set_enable(struct udevice *dev, bool enable)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->set_enable)
+ return -ENOSYS;
+
+ return ops->set_enable(dev, enable);
+}
+
+int regulator_get_mode(struct udevice *dev)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->get_mode)
+ return -ENOSYS;
+
+ return ops->get_mode(dev);
+}
+
+int regulator_set_mode(struct udevice *dev, int mode)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops || !ops->set_mode)
+ return -ENOSYS;
+
+ return ops->set_mode(dev, mode);
+}
+
+int regulator_get_by_platname(const char *plat_name, struct udevice **devp)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ struct udevice *dev;
+ int ret;
+
+ *devp = NULL;
+
+ for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev;
+ ret = uclass_find_next_device(&dev)) {
+ if (ret)
+ continue;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata || strcmp(plat_name, uc_pdata->name))
+ continue;
+
+ return uclass_get_device_tail(dev, 0, devp);
+ }
+
+ debug("%s: can't find: %s\n", __func__, plat_name);
+
+ return -ENODEV;
+}
+
+int regulator_get_by_devname(const char *devname, struct udevice **devp)
+{
+ return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp);
+}
+
+static int failed(int ret, bool verbose, const char *fmt, ...)
+{
+ va_list args;
+ char buf[64];
+
+ if (verbose == false)
+ return ret;
+
+ va_start(args, fmt);
+ vscnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ printf(buf);
+
+ if (!ret)
+ return 0;
+
+ printf(" (ret: %d)", ret);
+
+ return ret;
+}
+
+int regulator_autoset(const char *platname,
+ struct udevice **devp,
+ bool verbose)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ struct udevice *dev;
+ int ret;
+
+ if (devp)
+ *devp = NULL;
+
+ ret = regulator_get_by_platname(platname, &dev);
+ if (ret) {
+ error("Can get the regulator: %s!", platname);
+ return ret;
+ }
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata) {
+ error("Can get the regulator %s uclass platdata!", platname);
+ return -ENXIO;
+ }
+
+ if (!uc_pdata->always_on && !uc_pdata->boot_on)
+ goto retdev;
+
+ if (verbose)
+ printf("%s@%s: ", dev->name, uc_pdata->name);
+
+ /* Those values are optional (-ENODATA if unset) */
+ if ((uc_pdata->min_uV != -ENODATA) &&
+ (uc_pdata->max_uV != -ENODATA) &&
+ (uc_pdata->min_uV == uc_pdata->max_uV)) {
+ ret = regulator_set_value(dev, uc_pdata->min_uV);
+ if (failed(ret, verbose, "set %d uV", uc_pdata->min_uV))
+ goto exit;
+ }
+
+ /* Those values are optional (-ENODATA if unset) */
+ if ((uc_pdata->min_uA != -ENODATA) &&
+ (uc_pdata->max_uA != -ENODATA) &&
+ (uc_pdata->min_uA == uc_pdata->max_uA)) {
+ ret = regulator_set_current(dev, uc_pdata->min_uA);
+ if (failed(ret, verbose, "; set %d uA", uc_pdata->min_uA))
+ goto exit;
+ }
+
+ ret = regulator_set_enable(dev, true);
+ if (failed(ret, verbose, "; enabling", uc_pdata->min_uA))
+ goto exit;
+
+retdev:
+ if (devp)
+ *devp = dev;
+exit:
+ if (verbose)
+ printf("\n");
+
+ return ret;
+}
+
+int regulator_list_autoset(const char *list_platname[],
+ struct udevice *list_devp[],
+ bool verbose)
+{
+ struct udevice *dev;
+ int error = 0, i = 0, ret;
+
+ while (list_platname[i]) {
+ ret = regulator_autoset(list_platname[i], &dev, verbose);
+ if (ret & !error)
+ error = ret;
+
+ if (list_devp)
+ list_devp[i] = dev;
+
+ i++;
+ }
+
+ return error;
+}
+
+static bool regulator_name_is_unique(struct udevice *check_dev,
+ const char *check_name)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ struct udevice *dev;
+ int check_len = strlen(check_name);
+ int ret;
+ int len;
+
+ for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev;
+ ret = uclass_find_next_device(&dev)) {
+ if (ret || dev == check_dev)
+ continue;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ len = strlen(uc_pdata->name);
+ if (len != check_len)
+ continue;
+
+ if (!strcmp(uc_pdata->name, check_name))
+ return false;
+ }
+
+ return true;
+}
+
+static int regulator_post_bind(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ int offset = dev->of_offset;
+ const void *blob = gd->fdt_blob;
+ const char *property = "regulator-name";
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata)
+ return -ENXIO;
+
+ /* Regulator's mandatory constraint */
+ uc_pdata->name = fdt_getprop(blob, offset, property, NULL);
+ if (!uc_pdata->name) {
+ debug("%s: dev: %s has no property 'regulator-name'\n",
+ __func__, dev->name);
+ return -EINVAL;
+ }
+
+ if (regulator_name_is_unique(dev, uc_pdata->name))
+ return 0;
+
+ error("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"",
+ property, dev->name, uc_pdata->name);
+
+ return -EINVAL;
+}
+
+static int regulator_pre_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ int offset = dev->of_offset;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+ if (!uc_pdata)
+ return -ENXIO;
+
+ /* Regulator's optional constraints */
+ uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset,
+ "regulator-min-microvolt", -ENODATA);
+ uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset,
+ "regulator-max-microvolt", -ENODATA);
+ uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset,
+ "regulator-min-microamp", -ENODATA);
+ uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset,
+ "regulator-max-microamp", -ENODATA);
+ uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset,
+ "regulator-always-on");
+ uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset,
+ "regulator-boot-on");
+
+ return 0;
+}
+
+UCLASS_DRIVER(regulator) = {
+ .id = UCLASS_REGULATOR,
+ .name = "regulator",
+ .post_bind = regulator_post_bind,
+ .pre_probe = regulator_pre_probe,
+ .per_device_platdata_auto_alloc_size =
+ sizeof(struct dm_regulator_uclass_platdata),
+};
diff --git a/drivers/power/regulator/sandbox.c b/drivers/power/regulator/sandbox.c
new file mode 100644
index 0000000..2cca579
--- /dev/null
+++ b/drivers/power/regulator/sandbox.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/sandbox_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define MODE(_id, _val, _name) [_id] = { \
+ .id = _id, \
+ .register_value = _val, \
+ .name = _name, \
+}
+
+#define RANGE(_min, _max, _step) { \
+ .min = _min, \
+ .max = _max, \
+ .step = _step, \
+}
+
+/*
+ * struct output_range - helper structure type to define the range of output
+ * operating values (current/voltage), limited by the PMIC IC design.
+ *
+ * @min - minimum value
+ * @max - maximum value
+ * @step - step value
+*/
+struct output_range {
+ int min;
+ int max;
+ int step;
+};
+
+/* BUCK: 1,2 - voltage range */
+static struct output_range buck_voltage_range[] = {
+ RANGE(OUT_BUCK1_UV_MIN, OUT_BUCK1_UV_MAX, OUT_BUCK1_UV_STEP),
+ RANGE(OUT_BUCK2_UV_MIN, OUT_BUCK2_UV_MAX, OUT_BUCK2_UV_STEP),
+};
+
+/* BUCK: 1 - current range */
+static struct output_range buck_current_range[] = {
+ RANGE(OUT_BUCK1_UA_MIN, OUT_BUCK1_UA_MAX, OUT_BUCK1_UA_STEP),
+};
+
+/* BUCK operating modes */
+static struct dm_regulator_mode sandbox_buck_modes[] = {
+ MODE(BUCK_OM_OFF, OM2REG(BUCK_OM_OFF), "OFF"),
+ MODE(BUCK_OM_ON, OM2REG(BUCK_OM_ON), "ON"),
+ MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"),
+};
+
+/* LDO: 1,2 - voltage range */
+static struct output_range ldo_voltage_range[] = {
+ RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP),
+ RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP),
+};
+
+/* LDO: 1 - current range */
+static struct output_range ldo_current_range[] = {
+ RANGE(OUT_LDO1_UA_MIN, OUT_LDO1_UA_MAX, OUT_LDO1_UA_STEP),
+};
+
+/* LDO operating modes */
+static struct dm_regulator_mode sandbox_ldo_modes[] = {
+ MODE(LDO_OM_OFF, OM2REG(LDO_OM_OFF), "OFF"),
+ MODE(LDO_OM_ON, OM2REG(LDO_OM_ON), "ON"),
+ MODE(LDO_OM_SLEEP, OM2REG(LDO_OM_SLEEP), "SLEEP"),
+ MODE(LDO_OM_STANDBY, OM2REG(LDO_OM_STANDBY), "STANDBY"),
+};
+
+int out_get_value(struct udevice *dev, int output_count, int reg_type,
+ struct output_range *range)
+{
+ uint8_t reg_val;
+ uint reg;
+ int ret;
+
+ if (dev->driver_data > output_count) {
+ error("Unknown regulator number: %lu for PMIC %s!",
+ dev->driver_data, dev->name);
+ return -EINVAL;
+ }
+
+ reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type;
+ ret = pmic_read(dev->parent, reg, &reg_val, 1);
+ if (ret) {
+ error("PMIC read failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = REG2VAL(range[dev->driver_data - 1].min,
+ range[dev->driver_data - 1].step,
+ reg_val);
+
+ return ret;
+}
+
+static int out_set_value(struct udevice *dev, int output_count, int reg_type,
+ struct output_range *range, int value)
+{
+ uint8_t reg_val;
+ uint reg;
+ int ret;
+ int max_value;
+
+ if (dev->driver_data > output_count) {
+ error("Unknown regulator number: %lu for PMIC %s!",
+ dev->driver_data, dev->name);
+ return -EINVAL;
+ }
+
+ max_value = range[dev->driver_data - 1].max;
+ if (value > max_value) {
+ error("Wrong value for %s: %lu. Max is: %d.",
+ dev->name, dev->driver_data, max_value);
+ return -EINVAL;
+ }
+
+ reg_val = VAL2REG(range[dev->driver_data - 1].min,
+ range[dev->driver_data - 1].step,
+ value);
+
+ reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type;
+ ret = pmic_write(dev->parent, reg, &reg_val, 1);
+ if (ret) {
+ error("PMIC write failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int out_get_mode(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ uint8_t reg_val;
+ uint reg;
+ int ret;
+ int i;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM;
+ ret = pmic_read(dev->parent, reg, &reg_val, 1);
+ if (ret) {
+ error("PMIC read failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < uc_pdata->mode_count; i++) {
+ if (reg_val == uc_pdata->mode[i].register_value)
+ return uc_pdata->mode[i].id;
+ }
+
+ error("Unknown operation mode for %s!", dev->name);
+ return -EINVAL;
+}
+
+static int out_set_mode(struct udevice *dev, int mode)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ int reg_val = -1;
+ uint reg;
+ int ret;
+ int i;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ if (mode >= uc_pdata->mode_count)
+ return -EINVAL;
+
+ for (i = 0; i < uc_pdata->mode_count; i++) {
+ if (mode == uc_pdata->mode[i].id) {
+ reg_val = uc_pdata->mode[i].register_value;
+ break;
+ }
+ }
+
+ if (reg_val == -1) {
+ error("Unknown operation mode for %s!", dev->name);
+ return -EINVAL;
+ }
+
+ reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM;
+ ret = pmic_write(dev->parent, reg, (uint8_t *)&reg_val, 1);
+ if (ret) {
+ error("PMIC write failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int buck_get_voltage(struct udevice *dev)
+{
+ return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV,
+ buck_voltage_range);
+}
+
+static int buck_set_voltage(struct udevice *dev, int uV)
+{
+ return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV,
+ buck_voltage_range, uV);
+}
+
+static int buck_get_current(struct udevice *dev)
+{
+ /* BUCK2 - unsupported */
+ if (dev->driver_data == 2)
+ return -ENOSYS;
+
+ return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA,
+ buck_current_range);
+}
+
+static int buck_set_current(struct udevice *dev, int uA)
+{
+ /* BUCK2 - unsupported */
+ if (dev->driver_data == 2)
+ return -ENOSYS;
+
+ return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA,
+ buck_current_range, uA);
+}
+
+static bool buck_get_enable(struct udevice *dev)
+{
+ if (out_get_mode(dev) == BUCK_OM_OFF)
+ return false;
+
+ return true;
+}
+
+static int buck_set_enable(struct udevice *dev, bool enable)
+{
+ return out_set_mode(dev, enable ? BUCK_OM_ON : BUCK_OM_OFF);
+}
+
+static int sandbox_buck_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_BUCK;
+ uc_pdata->mode = sandbox_buck_modes;
+ uc_pdata->mode_count = ARRAY_SIZE(sandbox_buck_modes);
+
+ return 0;
+}
+
+static const struct dm_regulator_ops sandbox_buck_ops = {
+ .get_value = buck_get_voltage,
+ .set_value = buck_set_voltage,
+ .get_current = buck_get_current,
+ .set_current = buck_set_current,
+ .get_enable = buck_get_enable,
+ .set_enable = buck_set_enable,
+ .get_mode = out_get_mode,
+ .set_mode = out_set_mode,
+};
+
+U_BOOT_DRIVER(sandbox_buck) = {
+ .name = SANDBOX_BUCK_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &sandbox_buck_ops,
+ .probe = sandbox_buck_probe,
+};
+
+static int ldo_get_voltage(struct udevice *dev)
+{
+ return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV,
+ ldo_voltage_range);
+}
+
+static int ldo_set_voltage(struct udevice *dev, int uV)
+{
+ return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV,
+ ldo_voltage_range, uV);
+}
+
+static int ldo_get_current(struct udevice *dev)
+{
+ /* LDO2 - unsupported */
+ if (dev->driver_data == 2)
+ return -ENOSYS;
+
+ return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
+ ldo_current_range);
+}
+
+static int ldo_set_current(struct udevice *dev, int uA)
+{
+ /* LDO2 - unsupported */
+ if (dev->driver_data == 2)
+ return -ENOSYS;
+
+ return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
+ ldo_current_range, uA);
+}
+
+static bool ldo_get_enable(struct udevice *dev)
+{
+ if (out_get_mode(dev) == LDO_OM_OFF)
+ return false;
+
+ return true;
+}
+
+static int ldo_set_enable(struct udevice *dev, bool enable)
+{
+ return out_set_mode(dev, enable ? LDO_OM_ON : LDO_OM_OFF);
+}
+
+static int sandbox_ldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->mode = sandbox_ldo_modes;
+ uc_pdata->mode_count = ARRAY_SIZE(sandbox_ldo_modes);
+
+ return 0;
+}
+
+static const struct dm_regulator_ops sandbox_ldo_ops = {
+ .get_value = ldo_get_voltage,
+ .set_value = ldo_set_voltage,
+ .get_current = ldo_get_current,
+ .set_current = ldo_set_current,
+ .get_enable = ldo_get_enable,
+ .set_enable = ldo_set_enable,
+ .get_mode = out_get_mode,
+ .set_mode = out_set_mode,
+};
+
+U_BOOT_DRIVER(sandbox_ldo) = {
+ .name = SANDBOX_LDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &sandbox_ldo_ops,
+ .probe = sandbox_ldo_probe,
+};
diff --git a/drivers/pwm/pwm-imx-util.c b/drivers/pwm/pwm-imx-util.c
index f1d0b35..79d86028 100644
--- a/drivers/pwm/pwm-imx-util.c
+++ b/drivers/pwm/pwm-imx-util.c
@@ -56,7 +56,7 @@ int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c,
*prescale = *period_c / 0x10000 + 1;
*period_c /= *prescale;
- c = (unsigned long long)(*period_c * duty_ns);
+ c = *period_c * (unsigned long long)duty_ns;
do_div(c, period_ns);
*duty_c = c;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e69de29..bd63621 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -0,0 +1,8 @@
+config DM_RTC
+ bool "Enable Driver Model for RTC drivers"
+ depends on DM
+ help
+ Enable drver model for real-time-clock drivers. The RTC uclass
+ then provides the rtc_get()/rtc_set() interface, delegating to
+ drivers to perform the actual functions. See rtc.h for a
+ description of the API.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index fdcbc00..3092de1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -7,6 +7,8 @@
#ccflags-y += -DDEBUG
+obj-$(CONFIG_DM_RTC) += rtc-uclass.o
+
obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o
obj-$(CONFIG_RTC_BFIN) += bfin_rtc.o
obj-y += date.o
@@ -24,6 +26,7 @@ obj-$(CONFIG_RTC_DS164x) += ds164x.o
obj-$(CONFIG_RTC_DS174x) += ds174x.o
obj-$(CONFIG_RTC_DS3231) += ds3231.o
obj-$(CONFIG_RTC_FTRTC010) += ftrtc010.o
+obj-$(CONFIG_SANDBOX) += i2c_rtc_emul.o
obj-$(CONFIG_RTC_IMXDI) += imxdi.o
obj-$(CONFIG_RTC_ISL1208) += isl1208.o
obj-$(CONFIG_RTC_M41T11) += m41t11.o
@@ -49,4 +52,5 @@ obj-$(CONFIG_RTC_RTC4543) += rtc4543.o
obj-$(CONFIG_RTC_RV3029) += rv3029.o
obj-$(CONFIG_RTC_RX8025) += rx8025.o
obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o
+obj-$(CONFIG_SANDBOX) += sandbox_rtc.o
obj-$(CONFIG_RTC_X1205) += x1205.o
diff --git a/drivers/rtc/at91sam9_rtt.c b/drivers/rtc/at91sam9_rtt.c
index 714dd2a..a684ad6 100644
--- a/drivers/rtc/at91sam9_rtt.c
+++ b/drivers/rtc/at91sam9_rtt.c
@@ -44,7 +44,7 @@ int rtc_get (struct rtc_time *tmp)
} while (tim!=tim2);
off = readl(&gpbr->reg[AT91_GPBR_INDEX_TIMEOFF]);
/* off==0 means time is invalid, but we ignore that */
- to_tm (tim+off, tmp);
+ rtc_to_tm(tim+off, tmp);
return 0;
}
@@ -54,8 +54,7 @@ int rtc_set (struct rtc_time *tmp)
at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
ulong tim;
- tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ tim = rtc_mktime(tmp);
/* clear alarm, set prescaler to 32768, clear counter */
writel(32768+AT91_RTT_RTTRST, &rtt->mr);
diff --git a/drivers/rtc/bfin_rtc.c b/drivers/rtc/bfin_rtc.c
index 4cf2d83..a079a1d 100644
--- a/drivers/rtc/bfin_rtc.c
+++ b/drivers/rtc/bfin_rtc.c
@@ -67,8 +67,7 @@ int rtc_set(struct rtc_time *tmp)
wait_for_complete();
/* Calculate number of seconds this incoming time represents */
- remain = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ remain = rtc_mktime(tmp);
/* Figure out how many days since epoch */
days = remain / NUM_SECS_IN_DAY;
@@ -114,7 +113,7 @@ int rtc_get(struct rtc_time *tmp)
/* Calculate the total number of seconds since epoch */
time_in_sec = (tm_sec) + MIN_TO_SECS(tm_min) + HRS_TO_SECS(tm_hr) + DAYS_TO_SECS(tm_day);
- to_tm(time_in_sec, tmp);
+ rtc_to_tm(time_in_sec, tmp);
return 0;
}
diff --git a/drivers/rtc/date.c b/drivers/rtc/date.c
index 15e6db0..8c643a0 100644
--- a/drivers/rtc/date.c
+++ b/drivers/rtc/date.c
@@ -11,6 +11,7 @@
#include <common.h>
#include <command.h>
+#include <errno.h>
#include <rtc.h>
#if defined(CONFIG_CMD_DATE) || defined(CONFIG_TIMESTAMP)
@@ -30,13 +31,15 @@ static int month_days[12] = {
/*
* This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
*/
-void GregorianDay(struct rtc_time * tm)
+int rtc_calc_weekday(struct rtc_time *tm)
{
int leapsToDate;
int lastYear;
int day;
int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
+ if (tm->tm_year < 1753)
+ return -EINVAL;
lastYear=tm->tm_year-1;
/*
@@ -64,9 +67,11 @@ void GregorianDay(struct rtc_time * tm)
day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday;
tm->tm_wday=day%7;
+
+ return 0;
}
-void to_tm(int tim, struct rtc_time * tm)
+int rtc_to_tm(int tim, struct rtc_time *tm)
{
register int i;
register long hms, day;
@@ -98,10 +103,14 @@ void to_tm(int tim, struct rtc_time * tm)
/* Days are what is left over (+1) from all that. */
tm->tm_mday = day + 1;
+ /* Zero unused fields */
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+
/*
* Determine the day of week
*/
- GregorianDay(tm);
+ return rtc_calc_weekday(tm);
}
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
@@ -119,22 +128,23 @@ void to_tm(int tim, struct rtc_time * tm)
* machines were long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*/
-unsigned long
-mktime (unsigned int year, unsigned int mon,
- unsigned int day, unsigned int hour,
- unsigned int min, unsigned int sec)
+unsigned long rtc_mktime(const struct rtc_time *tm)
{
- if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ int mon = tm->tm_mon;
+ int year = tm->tm_year;
+ int days, hours;
+
+ mon -= 2;
+ if (0 >= (int)mon) { /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
- return (((
- (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
- year*365 - 719499
- )*24 + hour /* now have hours */
- )*60 + min /* now have minutes */
- )*60 + sec; /* finally seconds */
+ days = (unsigned long)(year / 4 - year / 100 + year / 400 +
+ 367 * mon / 12 + tm->tm_mday) +
+ year * 365 - 719499;
+ hours = days * 24 + tm->tm_hour;
+ return (hours * 60 + tm->tm_min) * 60 + tm->tm_sec;
}
#endif
diff --git a/drivers/rtc/ds1306.c b/drivers/rtc/ds1306.c
index 1ec1837..7dd3e19 100644
--- a/drivers/rtc/ds1306.c
+++ b/drivers/rtc/ds1306.c
@@ -110,7 +110,7 @@ int rtc_get (struct rtc_time *tmp)
immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */
udelay (10);
- GregorianDay (tmp); /* Determine the day of week */
+ rtc_calc_weekday(tmp); /* Determine the day of week */
debug ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
@@ -180,8 +180,7 @@ int rtc_set (struct rtc_time *tmp)
{
ulong tim;
- tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ tim = rtc_mktime(tmp);
immap->im_sitk.sitk_rtck = KAPWR_KEY;
immap->im_sit.sit_rtc = tim;
diff --git a/drivers/rtc/ds1374.c b/drivers/rtc/ds1374.c
index 427b1eb..7847357 100644
--- a/drivers/rtc/ds1374.c
+++ b/drivers/rtc/ds1374.c
@@ -118,7 +118,7 @@ int rtc_get (struct rtc_time *tm){
DEBUGR ("Get RTC s since 1.1.1970: %ld\n", time1);
- to_tm(time1, tm); /* To Gregorian Date */
+ rtc_to_tm(time1, tm); /* To Gregorian Date */
if (rtc_read(RTC_SR_ADDR) & RTC_SR_BIT_OSF) {
printf ("### Warning: RTC oscillator has stopped\n");
@@ -147,9 +147,7 @@ int rtc_set (struct rtc_time *tmp){
if (tmp->tm_year < 1970 || tmp->tm_year > 2069)
printf("WARNING: year should be between 1970 and 2069!\n");
- time = mktime(tmp->tm_year, tmp->tm_mon,
- tmp->tm_mday, tmp->tm_hour,
- tmp->tm_min, tmp->tm_sec);
+ time = rtc_mktime(tmp);
DEBUGR ("Set RTC s since 1.1.1970: %ld (0x%02lx)\n", time, time);
diff --git a/drivers/rtc/ftrtc010.c b/drivers/rtc/ftrtc010.c
index 713dad2..7d0cfb3 100644
--- a/drivers/rtc/ftrtc010.c
+++ b/drivers/rtc/ftrtc010.c
@@ -86,7 +86,7 @@ int rtc_get(struct rtc_time *tmp)
now = ftrtc010_time() + readl(&rtc->record);
#endif
- to_tm(now, tmp);
+ rtc_to_tm(now, tmp);
return 0;
}
@@ -104,8 +104,7 @@ int rtc_set(struct rtc_time *tmp)
tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
- new = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_hour,
- tmp->tm_min, tmp->tm_sec);
+ new = rtc_mktime(tmp);
now = ftrtc010_time();
diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c
new file mode 100644
index 0000000..20827fd
--- /dev/null
+++ b/drivers/rtc/i2c_rtc_emul.c
@@ -0,0 +1,236 @@
+/*
+ * Simulate an I2C real time clock
+ *
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * This is a test driver. It starts off with the current time of the machine,
+ * but also supports setting the time, using an offset from the current
+ * clock. This driver is only intended for testing, not accurate
+ * time-keeping. It does not change the system time.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <os.h>
+#include <rtc.h>
+#include <asm/rtc.h>
+#include <asm/test.h>
+
+#ifdef DEBUG
+#define debug_buffer print_buffer
+#else
+#define debug_buffer(x, ...)
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct sandbox_i2c_rtc_plat_data - platform data for the RTC
+ *
+ * @base_time: Base system time when RTC device was bound
+ * @offset: RTC offset from current system time
+ * @use_system_time: true to use system time, false to use @base_time
+ * @reg: Register values
+ */
+struct sandbox_i2c_rtc_plat_data {
+ long base_time;
+ long offset;
+ bool use_system_time;
+ u8 reg[REG_COUNT];
+};
+
+struct sandbox_i2c_rtc {
+ unsigned int offset_secs;
+};
+
+long sandbox_i2c_rtc_set_offset(struct udevice *dev, bool use_system_time,
+ int offset)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
+ long old_offset;
+
+ old_offset = plat->offset;
+ plat->use_system_time = use_system_time;
+ if (offset != -1)
+ plat->offset = offset;
+
+ return old_offset;
+}
+
+long sandbox_i2c_rtc_get_set_base_time(struct udevice *dev, long base_time)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
+ long old_base_time;
+
+ old_base_time = plat->base_time;
+ if (base_time != -1)
+ plat->base_time = base_time;
+
+ return old_base_time;
+}
+
+static void reset_time(struct udevice *dev)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
+ struct rtc_time now;
+
+ os_localtime(&now);
+ plat->base_time = rtc_mktime(&now);
+ plat->offset = 0;
+ plat->use_system_time = true;
+}
+
+static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
+ struct rtc_time tm_now;
+ long now;
+
+ if (plat->use_system_time) {
+ os_localtime(&tm_now);
+ now = rtc_mktime(&tm_now);
+ } else {
+ now = plat->base_time;
+ }
+
+ return rtc_to_tm(now + plat->offset, time);
+}
+
+static int sandbox_i2c_rtc_set(struct udevice *dev, const struct rtc_time *time)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(dev);
+ struct rtc_time tm_now;
+ long now;
+
+ if (plat->use_system_time) {
+ os_localtime(&tm_now);
+ now = rtc_mktime(&tm_now);
+ } else {
+ now = plat->base_time;
+ }
+ plat->offset = rtc_mktime(time) - now;
+
+ return 0;
+}
+
+/* Update the current time in the registers */
+static int sandbox_i2c_rtc_prepare_read(struct udevice *emul)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
+ struct rtc_time time;
+ int ret;
+
+ ret = sandbox_i2c_rtc_get(emul, &time);
+ if (ret)
+ return ret;
+
+ plat->reg[REG_SEC] = time.tm_sec;
+ plat->reg[REG_MIN] = time.tm_min;
+ plat->reg[REG_HOUR] = time.tm_hour;
+ plat->reg[REG_MDAY] = time.tm_mday;
+ plat->reg[REG_MON] = time.tm_mon;
+ plat->reg[REG_YEAR] = time.tm_year - 1900;
+ plat->reg[REG_WDAY] = time.tm_wday;
+
+ return 0;
+}
+
+static int sandbox_i2c_rtc_complete_write(struct udevice *emul)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
+ struct rtc_time time;
+ int ret;
+
+ time.tm_sec = plat->reg[REG_SEC];
+ time.tm_min = plat->reg[REG_MIN];
+ time.tm_hour = plat->reg[REG_HOUR];
+ time.tm_mday = plat->reg[REG_MDAY];
+ time.tm_mon = plat->reg[REG_MON];
+ time.tm_year = plat->reg[REG_YEAR] + 1900;
+ time.tm_wday = plat->reg[REG_WDAY];
+
+ ret = sandbox_i2c_rtc_set(emul, &time);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg,
+ int nmsgs)
+{
+ struct sandbox_i2c_rtc_plat_data *plat = dev_get_platdata(emul);
+ uint offset = 0;
+ int ret;
+
+ debug("\n%s\n", __func__);
+ ret = sandbox_i2c_rtc_prepare_read(emul);
+ if (ret)
+ return ret;
+ for (; nmsgs > 0; nmsgs--, msg++) {
+ int len;
+ u8 *ptr;
+
+ len = msg->len;
+ debug(" %s: msg->len=%d",
+ msg->flags & I2C_M_RD ? "read" : "write",
+ msg->len);
+ if (msg->flags & I2C_M_RD) {
+ debug(", offset %x, len %x: ", offset, len);
+
+ /* Read the register */
+ memcpy(msg->buf, plat->reg + offset, len);
+ memset(msg->buf + len, '\xff', msg->len - len);
+ debug_buffer(0, msg->buf, 1, msg->len, 0);
+ } else if (len >= 1) {
+ ptr = msg->buf;
+ offset = *ptr++ & (REG_COUNT - 1);
+ len--;
+ debug(", set offset %x: ", offset);
+ debug_buffer(0, msg->buf, 1, msg->len, 0);
+
+ /* Write the register */
+ memcpy(plat->reg + offset, ptr, len);
+ if (offset == REG_RESET)
+ reset_time(emul);
+ }
+ }
+ ret = sandbox_i2c_rtc_complete_write(emul);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct dm_i2c_ops sandbox_i2c_rtc_emul_ops = {
+ .xfer = sandbox_i2c_rtc_xfer,
+};
+
+static int sandbox_i2c_rtc_bind(struct udevice *dev)
+{
+ reset_time(dev);
+
+ return 0;
+}
+
+static const struct udevice_id sandbox_i2c_rtc_ids[] = {
+ { .compatible = "sandbox,i2c-rtc" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_i2c_rtc_emul) = {
+ .name = "sandbox_i2c_rtc_emul",
+ .id = UCLASS_I2C_EMUL,
+ .of_match = sandbox_i2c_rtc_ids,
+ .bind = sandbox_i2c_rtc_bind,
+ .priv_auto_alloc_size = sizeof(struct sandbox_i2c_rtc),
+ .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_rtc_plat_data),
+ .ops = &sandbox_i2c_rtc_emul_ops,
+};
diff --git a/drivers/rtc/imxdi.c b/drivers/rtc/imxdi.c
index 0d7d736..17519ce 100644
--- a/drivers/rtc/imxdi.c
+++ b/drivers/rtc/imxdi.c
@@ -192,7 +192,7 @@ int rtc_get(struct rtc_time *tmp)
}
now = __raw_readl(&data.regs->dtcmr);
- to_tm(now, tmp);
+ rtc_to_tm(now, tmp);
err:
return rc;
@@ -209,8 +209,7 @@ int rtc_set(struct rtc_time *tmp)
goto err;
}
- now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ now = rtc_mktime(tmp);
/* zero the fractional part first */
rc = DI_WRITE_WAIT(0, dtclr);
if (rc == 0)
diff --git a/drivers/rtc/mc13xxx-rtc.c b/drivers/rtc/mc13xxx-rtc.c
index 528247a..3e46336 100644
--- a/drivers/rtc/mc13xxx-rtc.c
+++ b/drivers/rtc/mc13xxx-rtc.c
@@ -36,7 +36,7 @@ int rtc_get(struct rtc_time *rtc)
tim = day1 * 86400 + time;
- to_tm(tim, rtc);
+ rtc_to_tm(tim, rtc);
rtc->tm_yday = 0;
rtc->tm_isdst = 0;
@@ -51,8 +51,7 @@ int rtc_set(struct rtc_time *rtc)
if (!p)
return -1;
- time = mktime(rtc->tm_year, rtc->tm_mon, rtc->tm_mday,
- rtc->tm_hour, rtc->tm_min, rtc->tm_sec);
+ time = rtc_mktime(rtc);
day = time / 86400;
time %= 86400;
diff --git a/drivers/rtc/mcfrtc.c b/drivers/rtc/mcfrtc.c
index 8961ca4..e02e297 100644
--- a/drivers/rtc/mcfrtc.c
+++ b/drivers/rtc/mcfrtc.c
@@ -38,7 +38,7 @@ int rtc_get(struct rtc_time *tmp)
tim = (tim * 60) + rtc_mins;
tim = (tim * 60) + rtc->seconds;
- to_tm(tim, tmp);
+ rtc_to_tm(tim, tmp);
tmp->tm_yday = 0;
tmp->tm_isdst = 0;
diff --git a/drivers/rtc/mpc8xx.c b/drivers/rtc/mpc8xx.c
index d239dae..147a225 100644
--- a/drivers/rtc/mpc8xx.c
+++ b/drivers/rtc/mpc8xx.c
@@ -26,7 +26,7 @@ int rtc_get (struct rtc_time *tmp)
tim = immr->im_sit.sit_rtc;
- to_tm (tim, tmp);
+ rtc_to_tm(tim, tmp);
debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
@@ -44,8 +44,7 @@ int rtc_set (struct rtc_time *tmp)
tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
- tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ tim = rtc_mktime(tmp);
immr->im_sitk.sitk_rtck = KAPWR_KEY;
immr->im_sit.sit_rtc = tim;
diff --git a/drivers/rtc/mx27rtc.c b/drivers/rtc/mx27rtc.c
index ae6595b..29ccdf1 100644
--- a/drivers/rtc/mx27rtc.c
+++ b/drivers/rtc/mx27rtc.c
@@ -30,7 +30,7 @@ int rtc_get(struct rtc_time *time)
sec += min * 60 + hour * 3600 + day * 24 * 3600;
- to_tm(sec, time);
+ rtc_to_tm(sec, time);
return 0;
}
@@ -40,8 +40,7 @@ int rtc_set(struct rtc_time *time)
struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE;
uint32_t day, hour, min, sec;
- sec = mktime(time->tm_year, time->tm_mon, time->tm_mday,
- time->tm_hour, time->tm_min, time->tm_sec);
+ sec = rtc_mktime(time);
day = sec / (24 * 3600);
sec = sec % (24 * 3600);
diff --git a/drivers/rtc/mxsrtc.c b/drivers/rtc/mxsrtc.c
index 32ba8a3..6e32154 100644
--- a/drivers/rtc/mxsrtc.c
+++ b/drivers/rtc/mxsrtc.c
@@ -43,7 +43,7 @@ int rtc_get(struct rtc_time *time)
uint32_t secs;
secs = readl(&rtc_regs->hw_rtc_seconds);
- to_tm(secs, time);
+ rtc_to_tm(secs, time);
return 0;
}
@@ -52,8 +52,7 @@ int rtc_set(struct rtc_time *time)
{
uint32_t secs;
- secs = mktime(time->tm_year, time->tm_mon, time->tm_mday,
- time->tm_hour, time->tm_min, time->tm_sec);
+ secs = rtc_mktime(time);
return mxs_rtc_set_time(secs);
}
diff --git a/drivers/rtc/pl031.c b/drivers/rtc/pl031.c
index c4d1259..fc83049 100644
--- a/drivers/rtc/pl031.c
+++ b/drivers/rtc/pl031.c
@@ -72,8 +72,7 @@ int rtc_set(struct rtc_time *tmp)
}
/* Calculate number of seconds this incoming time represents */
- tim = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ tim = rtc_mktime(tmp);
RTC_WRITE_REG(RTC_LR, tim);
@@ -97,7 +96,7 @@ int rtc_get(struct rtc_time *tmp)
tim = RTC_READ_REG(RTC_DR);
- to_tm (tim, tmp);
+ rtc_to_tm(tim, tmp);
debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
diff --git a/drivers/rtc/rtc-uclass.c b/drivers/rtc/rtc-uclass.c
new file mode 100644
index 0000000..fe74c69
--- /dev/null
+++ b/drivers/rtc/rtc-uclass.c
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <rtc.h>
+
+int dm_rtc_get(struct udevice *dev, struct rtc_time *time)
+{
+ struct rtc_ops *ops = rtc_get_ops(dev);
+
+ assert(ops);
+ if (!ops->get)
+ return -ENOSYS;
+ return ops->get(dev, time);
+}
+
+int dm_rtc_set(struct udevice *dev, struct rtc_time *time)
+{
+ struct rtc_ops *ops = rtc_get_ops(dev);
+
+ assert(ops);
+ if (!ops->set)
+ return -ENOSYS;
+ return ops->set(dev, time);
+}
+
+int dm_rtc_reset(struct udevice *dev)
+{
+ struct rtc_ops *ops = rtc_get_ops(dev);
+
+ assert(ops);
+ if (!ops->reset)
+ return -ENOSYS;
+ return ops->reset(dev);
+}
+
+int rtc_read8(struct udevice *dev, unsigned int reg)
+{
+ struct rtc_ops *ops = rtc_get_ops(dev);
+
+ assert(ops);
+ if (!ops->read8)
+ return -ENOSYS;
+ return ops->read8(dev, reg);
+}
+
+int rtc_write8(struct udevice *dev, unsigned int reg, int val)
+{
+ struct rtc_ops *ops = rtc_get_ops(dev);
+
+ assert(ops);
+ if (!ops->write8)
+ return -ENOSYS;
+ return ops->write8(dev, reg, val);
+}
+
+int rtc_read32(struct udevice *dev, unsigned int reg, u32 *valuep)
+{
+ u32 value = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < sizeof(value); i++) {
+ ret = rtc_read8(dev, reg + i);
+ if (ret)
+ return ret;
+ value |= ret << (i << 3);
+ }
+
+ *valuep = value;
+ return 0;
+}
+
+int rtc_write32(struct udevice *dev, unsigned int reg, u32 value)
+{
+ int i, ret;
+
+ for (i = 0; i < sizeof(value); i++) {
+ ret = rtc_write8(dev, reg + i, (value >> (i << 3)) & 0xff);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+UCLASS_DRIVER(rtc) = {
+ .name = "rtc",
+ .id = UCLASS_RTC,
+};
diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c
new file mode 100644
index 0000000..f292fbe
--- /dev/null
+++ b/drivers/rtc/sandbox_rtc.c
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <rtc.h>
+#include <asm/rtc.h>
+
+#define REG_COUNT 0x80
+
+static int sandbox_rtc_get(struct udevice *dev, struct rtc_time *time)
+{
+ time->tm_sec = dm_i2c_reg_read(dev, REG_SEC);
+ if (time->tm_sec < 0)
+ return time->tm_sec;
+ time->tm_min = dm_i2c_reg_read(dev, REG_MIN);
+ if (time->tm_min < 0)
+ return time->tm_min;
+ time->tm_hour = dm_i2c_reg_read(dev, REG_HOUR);
+ if (time->tm_hour < 0)
+ return time->tm_hour;
+ time->tm_mday = dm_i2c_reg_read(dev, REG_MDAY);
+ if (time->tm_mday < 0)
+ return time->tm_mday;
+ time->tm_mon = dm_i2c_reg_read(dev, REG_MON);
+ if (time->tm_mon < 0)
+ return time->tm_mon;
+ time->tm_year = dm_i2c_reg_read(dev, REG_YEAR);
+ if (time->tm_year < 0)
+ return time->tm_year;
+ time->tm_year += 1900;
+ time->tm_wday = dm_i2c_reg_read(dev, REG_WDAY);
+ if (time->tm_wday < 0)
+ return time->tm_wday;
+
+ return 0;
+}
+
+static int sandbox_rtc_set(struct udevice *dev, const struct rtc_time *time)
+{
+ int ret;
+
+ ret = dm_i2c_reg_write(dev, REG_SEC, time->tm_sec);
+ if (ret < 0)
+ return ret;
+ ret = dm_i2c_reg_write(dev, REG_MIN, time->tm_min);
+ if (ret < 0)
+ return ret;
+ ret = dm_i2c_reg_write(dev, REG_HOUR, time->tm_hour);
+ if (ret < 0)
+ return ret;
+ ret = dm_i2c_reg_write(dev, REG_MDAY, time->tm_mday);
+ if (ret < 0)
+ return ret;
+ ret = dm_i2c_reg_write(dev, REG_MON, time->tm_mon);
+ if (ret < 0)
+ return ret;
+ ret = dm_i2c_reg_write(dev, REG_YEAR, time->tm_year - 1900);
+ if (ret < 0)
+ return ret;
+ ret = dm_i2c_reg_write(dev, REG_WDAY, time->tm_wday);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int sandbox_rtc_reset(struct udevice *dev)
+{
+ return dm_i2c_reg_write(dev, REG_RESET, 0);
+}
+
+static int sandbox_rtc_read8(struct udevice *dev, unsigned int reg)
+{
+ return dm_i2c_reg_read(dev, reg);
+}
+
+static int sandbox_rtc_write8(struct udevice *dev, unsigned int reg, int val)
+{
+ return dm_i2c_reg_write(dev, reg, val);
+}
+
+static const struct rtc_ops sandbox_rtc_ops = {
+ .get = sandbox_rtc_get,
+ .set = sandbox_rtc_set,
+ .reset = sandbox_rtc_reset,
+ .read8 = sandbox_rtc_read8,
+ .write8 = sandbox_rtc_write8,
+};
+
+static const struct udevice_id sandbox_rtc_ids[] = {
+ { .compatible = "sandbox-rtc" },
+ { }
+};
+
+U_BOOT_DRIVER(rtc_sandbox) = {
+ .name = "rtc-sandbox",
+ .id = UCLASS_RTC,
+ .of_match = sandbox_rtc_ids,
+ .ops = &sandbox_rtc_ops,
+};
diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c
index 2124161..ad503af 100644
--- a/drivers/serial/serial_pl01x.c
+++ b/drivers/serial/serial_pl01x.c
@@ -20,6 +20,9 @@
#include <dm/platform_data/serial_pl01x.h>
#include <linux/compiler.h>
#include "serial_pl01x_internal.h"
+#include <fdtdec.h>
+
+DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_DM_SERIAL
@@ -28,7 +31,6 @@ static enum pl01x_type pl01x_type __attribute__ ((section(".data")));
static struct pl01x_regs *base_regs __attribute__ ((section(".data")));
#define NUM_PORTS (sizeof(port)/sizeof(port[0]))
-DECLARE_GLOBAL_DATA_PTR;
#endif
static int pl01x_putc(struct pl01x_regs *regs, char c)
@@ -351,9 +353,35 @@ static const struct dm_serial_ops pl01x_serial_ops = {
.setbrg = pl01x_serial_setbrg,
};
+#ifdef CONFIG_OF_CONTROL
+static const struct udevice_id pl01x_serial_id[] ={
+ {.compatible = "arm,pl011", .data = TYPE_PL011},
+ {.compatible = "arm,pl010", .data = TYPE_PL010},
+ {}
+};
+
+static int pl01x_serial_ofdata_to_platdata(struct udevice *dev)
+{
+ struct pl01x_serial_platdata *plat = dev_get_platdata(dev);
+ fdt_addr_t addr;
+
+ addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->base = addr;
+ plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "clock", 1);
+ plat->type = dev_get_driver_data(dev);
+ return 0;
+}
+#endif
+
U_BOOT_DRIVER(serial_pl01x) = {
.name = "serial_pl01x",
.id = UCLASS_SERIAL,
+ .of_match = of_match_ptr(pl01x_serial_id),
+ .ofdata_to_platdata = of_match_ptr(pl01x_serial_ofdata_to_platdata),
+ .platdata_auto_alloc_size = sizeof(struct pl01x_serial_platdata),
.probe = pl01x_serial_probe,
.ops = &pl01x_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index 3c80096..1b22c69 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -10,11 +10,34 @@
#include <serial.h>
#include <asm/arch/stm32.h>
+/*
+ * Set up the usart port
+ */
+#if (CONFIG_STM32_USART >= 1) && (CONFIG_STM32_USART <= 6)
+#define USART_PORT (CONFIG_STM32_USART - 1)
+#else
+#define USART_PORT 0
+#endif
+/*
+ * Set up the usart base address
+ *
+ * --STM32_USARTD_BASE means default setting
+ */
#define STM32_USART1_BASE (STM32_APB2PERIPH_BASE + 0x1000)
-#define RCC_APB2ENR_USART1EN (1 << 4)
-
-#define USART_BASE STM32_USART1_BASE
-#define RCC_USART_ENABLE RCC_APB2ENR_USART1EN
+#define STM32_USART2_BASE (STM32_APB1PERIPH_BASE + 0x4400)
+#define STM32_USART3_BASE (STM32_APB1PERIPH_BASE + 0x4800)
+#define STM32_USART6_BASE (STM32_APB2PERIPH_BASE + 0x1400)
+#define STM32_USARTD_BASE STM32_USART1_BASE
+/*
+ * RCC USART specific definitions
+ *
+ * --RCC_ENR_USARTDEN means default setting
+ */
+#define RCC_ENR_USART1EN (1 << 4)
+#define RCC_ENR_USART2EN (1 << 17)
+#define RCC_ENR_USART3EN (1 << 18)
+#define RCC_ENR_USART6EN (1 << 5)
+#define RCC_ENR_USARTDEN RCC_ENR_USART1EN
struct stm32_serial {
u32 sr;
@@ -39,6 +62,24 @@ struct stm32_serial {
DECLARE_GLOBAL_DATA_PTR;
+static const unsigned long usart_base[] = {
+ STM32_USART1_BASE,
+ STM32_USART2_BASE,
+ STM32_USART3_BASE,
+ STM32_USARTD_BASE,
+ STM32_USARTD_BASE,
+ STM32_USART6_BASE
+};
+
+static const unsigned long rcc_enr_en[] = {
+ RCC_ENR_USART1EN,
+ RCC_ENR_USART2EN,
+ RCC_ENR_USART3EN,
+ RCC_ENR_USARTDEN,
+ RCC_ENR_USARTDEN,
+ RCC_ENR_USART6EN
+};
+
static void stm32_serial_setbrg(void)
{
serial_init();
@@ -46,14 +87,17 @@ static void stm32_serial_setbrg(void)
static int stm32_serial_init(void)
{
- struct stm32_serial *usart = (struct stm32_serial *)USART_BASE;
+ struct stm32_serial *usart =
+ (struct stm32_serial *)usart_base[USART_PORT];
u32 clock, int_div, frac_div, tmp;
- if ((USART_BASE & STM32_BUS_MASK) == STM32_APB1PERIPH_BASE) {
- setbits_le32(&STM32_RCC->apb1enr, RCC_USART_ENABLE);
+ if ((usart_base[USART_PORT] & STM32_BUS_MASK) ==
+ STM32_APB1PERIPH_BASE) {
+ setbits_le32(&STM32_RCC->apb1enr, rcc_enr_en[USART_PORT]);
clock = clock_get(CLOCK_APB1);
- } else if ((USART_BASE & STM32_BUS_MASK) == STM32_APB2PERIPH_BASE) {
- setbits_le32(&STM32_RCC->apb2enr, RCC_USART_ENABLE);
+ } else if ((usart_base[USART_PORT] & STM32_BUS_MASK) ==
+ STM32_APB2PERIPH_BASE) {
+ setbits_le32(&STM32_RCC->apb2enr, rcc_enr_en[USART_PORT]);
clock = clock_get(CLOCK_APB2);
} else {
return -1;
@@ -72,7 +116,8 @@ static int stm32_serial_init(void)
static int stm32_serial_getc(void)
{
- struct stm32_serial *usart = (struct stm32_serial *)USART_BASE;
+ struct stm32_serial *usart =
+ (struct stm32_serial *)usart_base[USART_PORT];
while ((readl(&usart->sr) & USART_SR_FLAG_RXNE) == 0)
;
return readl(&usart->dr);
@@ -80,7 +125,9 @@ static int stm32_serial_getc(void)
static void stm32_serial_putc(const char c)
{
- struct stm32_serial *usart = (struct stm32_serial *)USART_BASE;
+ struct stm32_serial *usart =
+ (struct stm32_serial *)usart_base[USART_PORT];
+
while ((readl(&usart->sr) & USART_SR_FLAG_TXE) == 0)
;
writel(c, &usart->dr);
@@ -88,7 +135,8 @@ static void stm32_serial_putc(const char c)
static int stm32_serial_tstc(void)
{
- struct stm32_serial *usart = (struct stm32_serial *)USART_BASE;
+ struct stm32_serial *usart =
+ (struct stm32_serial *)usart_base[USART_PORT];
u8 ret;
ret = readl(&usart->sr) & USART_SR_FLAG_RXNE;
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 83fe8e0..737ae64 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -63,9 +63,12 @@ int spi_claim_bus(struct spi_slave *slave)
}
if (!speed)
speed = 100000;
- ret = spi_set_speed_mode(bus, speed, slave->mode);
- if (ret)
- return ret;
+ if (speed != slave->speed) {
+ ret = spi_set_speed_mode(bus, speed, slave->mode);
+ if (ret)
+ return ret;
+ slave->speed = speed;
+ }
return ops->claim_bus ? ops->claim_bus(dev) : 0;
}
diff --git a/drivers/tpm/tpm.c b/drivers/tpm/tpm.c
index 31761ec..a650892 100644
--- a/drivers/tpm/tpm.c
+++ b/drivers/tpm/tpm.c
@@ -34,6 +34,7 @@
#include <config.h>
#include <common.h>
+#include <dm.h>
#include <linux/compiler.h>
#include <fdtdec.h>
#include <i2c.h>
@@ -48,10 +49,14 @@ DECLARE_GLOBAL_DATA_PTR;
/* TPM configuration */
struct tpm {
+#ifdef CONFIG_DM_I2C
+ struct udevice *dev;
+#else
int i2c_bus;
int slave_addr;
- char inited;
int old_bus;
+#endif
+ char inited;
} tpm;
/* Global structure for tpm chip data */
@@ -372,7 +377,7 @@ static unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
{
- ssize_t rc;
+ int rc;
u32 count, ordinal;
unsigned long start, stop;
@@ -391,9 +396,11 @@ static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
return -E2BIG;
}
+ debug("Calling send\n");
rc = chip->vendor.send(chip, (u8 *)buf, count);
+ debug(" ... done calling send\n");
if (rc < 0) {
- error("tpm_transmit: tpm_send: error %zd\n", rc);
+ error("tpm_transmit: tpm_send: error %d\n", rc);
goto out;
}
@@ -403,7 +410,7 @@ static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
start = get_timer(0);
stop = tpm_calc_ordinal_duration(chip, ordinal);
do {
- debug("waiting for status...\n");
+ debug("waiting for status... %ld %ld\n", start, stop);
u8 status = chip->vendor.status(chip);
if ((status & chip->vendor.req_complete_mask) ==
chip->vendor.req_complete_val) {
@@ -428,15 +435,30 @@ out_recv:
debug("out_recv: reading response...\n");
rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE);
if (rc < 0)
- error("tpm_transmit: tpm_recv: error %zd\n", rc);
+ error("tpm_transmit: tpm_recv: error %d\n", rc);
out:
return rc;
}
+#ifdef CONFIG_DM_I2C
+static int tpm_open_dev(struct udevice *dev)
+{
+ int rc;
+
+ debug("%s: start\n", __func__);
+ if (g_chip.is_open)
+ return -EBUSY;
+ rc = tpm_vendor_init_dev(dev);
+ if (rc < 0)
+ g_chip.is_open = 0;
+ return rc;
+}
+#else
static int tpm_open(uint32_t dev_addr)
{
int rc;
+
if (g_chip.is_open)
return -EBUSY;
rc = tpm_vendor_init(dev_addr);
@@ -444,7 +466,7 @@ static int tpm_open(uint32_t dev_addr)
g_chip.is_open = 0;
return rc;
}
-
+#endif
static void tpm_close(void)
{
if (g_chip.is_open) {
@@ -455,6 +477,7 @@ static void tpm_close(void)
static int tpm_select(void)
{
+#ifndef CONFIG_DM_I2C
int ret;
tpm.old_bus = i2c_get_bus_num();
@@ -466,11 +489,13 @@ static int tpm_select(void)
return -1;
}
}
+#endif
return 0;
}
static int tpm_deselect(void)
{
+#ifndef CONFIG_DM_I2C
int ret;
if (tpm.old_bus != i2c_get_bus_num()) {
@@ -482,6 +507,7 @@ static int tpm_deselect(void)
}
}
tpm.old_bus = -1;
+#endif
return 0;
}
@@ -493,10 +519,9 @@ static int tpm_deselect(void)
*/
static int tpm_decode_config(struct tpm *dev)
{
-#ifdef CONFIG_OF_CONTROL
const void *blob = gd->fdt_blob;
- int node, parent;
- int i2c_bus;
+ int parent;
+ int node;
node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM);
if (node < 0) {
@@ -512,15 +537,48 @@ static int tpm_decode_config(struct tpm *dev)
debug("%s: Cannot find node parent\n", __func__);
return -1;
}
+#ifdef CONFIG_DM_I2C
+ struct udevice *bus;
+ int chip_addr;
+ int ret;
+
+ /*
+ * TODO(sjg@chromium.org): Remove this when driver model supports
+ * TPMs
+ */
+ ret = uclass_get_device_by_of_offset(UCLASS_I2C, parent, &bus);
+ if (ret) {
+ debug("Cannot find bus for node '%s: ret=%d'\n",
+ fdt_get_name(blob, parent, NULL), ret);
+ return ret;
+ }
+
+ chip_addr = fdtdec_get_int(blob, node, "reg", -1);
+ if (chip_addr == -1) {
+ debug("Cannot find reg property for node '%s: ret=%d'\n",
+ fdt_get_name(blob, node, NULL), ret);
+ return ret;
+ }
+ /*
+ * TODO(sjg@chromium.org): Older TPMs will need to use the older method
+ * in iic_tpm_read() so the offset length needs to be 0 here.
+ */
+ ret = i2c_get_chip(bus, chip_addr, 1, &dev->dev);
+ if (ret) {
+ debug("Cannot find device for node '%s: ret=%d'\n",
+ fdt_get_name(blob, node, NULL), ret);
+ return ret;
+ }
+#else
+ int i2c_bus;
+
i2c_bus = i2c_get_bus_num_fdt(parent);
if (i2c_bus < 0)
return -1;
dev->i2c_bus = i2c_bus;
dev->slave_addr = fdtdec_get_addr(blob, node, "reg");
-#else
- dev->i2c_bus = CONFIG_TPM_TIS_I2C_BUS_NUMBER;
- dev->slave_addr = CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS;
#endif
+
return 0;
}
@@ -547,6 +605,7 @@ int tis_init(void)
if (tpm_select())
return -1;
+#ifndef CONFIG_DM_I2C
/*
* Probe TPM twice; the first probing might fail because TPM is asleep,
* and the probing can wake up TPM.
@@ -556,8 +615,10 @@ int tis_init(void)
tpm.slave_addr);
return -1;
}
+#endif
tpm_deselect();
+ debug("%s: done\n", __func__);
tpm.inited = 1;
@@ -574,7 +635,11 @@ int tis_open(void)
if (tpm_select())
return -1;
+#ifdef CONFIG_DM_I2C
+ rc = tpm_open_dev(tpm.dev);
+#else
rc = tpm_open(tpm.slave_addr);
+#endif
tpm_deselect();
diff --git a/drivers/tpm/tpm_private.h b/drivers/tpm/tpm_private.h
index 888a074..8894c98 100644
--- a/drivers/tpm/tpm_private.h
+++ b/drivers/tpm/tpm_private.h
@@ -131,6 +131,9 @@ struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *);
int tpm_vendor_init(uint32_t dev_addr);
+struct udevice;
+int tpm_vendor_init_dev(struct udevice *dev);
+
void tpm_vendor_cleanup(struct tpm_chip *chip);
diff --git a/drivers/tpm/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c
index c1bbed4..ee4dfea 100644
--- a/drivers/tpm/tpm_tis_i2c.c
+++ b/drivers/tpm/tpm_tis_i2c.c
@@ -37,6 +37,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <fdtdec.h>
#include <linux/compiler.h>
#include <i2c.h>
@@ -122,13 +123,19 @@ static const char * const chip_name[] = {
/* Structure to store I2C TPM specific stuff */
struct tpm_dev {
+#ifdef CONFIG_DM_I2C
+ struct udevice *dev;
+#else
uint addr;
+#endif
u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */
enum i2c_chip_type chip_type;
};
static struct tpm_dev tpm_dev = {
+#ifndef CONFIG_DM_I2C
.addr = TPM_I2C_ADDR
+#endif
};
static struct tpm_dev tpm_dev;
@@ -156,8 +163,12 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) {
/* slb9635 protocol should work in both cases */
for (count = 0; count < MAX_COUNT; count++) {
+#ifdef CONFIG_DM_I2C
+ rc = dm_i2c_write(tpm_dev.dev, 0, (uchar *)&addrbuf, 1);
+#else
rc = i2c_write(tpm_dev.addr, 0, 0,
(uchar *)&addrbuf, 1);
+#endif
if (rc == 0)
break; /* Success, break to skip sleep */
udelay(SLEEP_DURATION);
@@ -171,7 +182,11 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
*/
for (count = 0; count < MAX_COUNT; count++) {
udelay(SLEEP_DURATION);
+#ifdef CONFIG_DM_I2C
+ rc = dm_i2c_read(tpm_dev.dev, 0, buffer, len);
+#else
rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len);
+#endif
if (rc == 0)
break; /* success, break to skip sleep */
}
@@ -184,7 +199,11 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
* be safe on the safe side.
*/
for (count = 0; count < MAX_COUNT; count++) {
+#ifdef CONFIG_DM_I2C
+ rc = dm_i2c_read(tpm_dev.dev, addr, buffer, len);
+#else
rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len);
+#endif
if (rc == 0)
break; /* break here to skip sleep */
udelay(SLEEP_DURATION);
@@ -206,18 +225,26 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
int count;
/* Prepare send buffer */
+#ifndef CONFIG_DM_I2C
tpm_dev.buf[0] = addr;
memcpy(&(tpm_dev.buf[1]), buffer, len);
+ buffer = tpm_dev.buf;
+ len++;
+#endif
for (count = 0; count < max_count; count++) {
- rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1);
+#ifdef CONFIG_DM_I2C
+ rc = dm_i2c_write(tpm_dev.dev, addr, buffer, len);
+#else
+ rc = i2c_write(tpm_dev.addr, 0, 0, buffer, len);
+#endif
if (rc == 0)
break; /* Success, break to skip sleep */
udelay(sleep_time);
}
/* take care of 'guard time' */
- udelay(SLEEP_DURATION);
+ udelay(sleep_time);
if (rc)
return -rc;
@@ -292,11 +319,14 @@ static int request_locality(struct tpm_chip *chip, int loc)
{
unsigned long start, stop;
u8 buf = TPM_ACCESS_REQUEST_USE;
+ int rc;
if (check_locality(chip, loc) >= 0)
return loc; /* We already have the locality */
- iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+ rc = iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+ if (rc)
+ return rc;
/* Wait for burstcount */
start = get_timer(0);
@@ -323,10 +353,15 @@ static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
static void tpm_tis_i2c_ready(struct tpm_chip *chip)
{
+ int rc;
+
/* This causes the current command to be aborted */
u8 buf = TPM_STS_COMMAND_READY;
- iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+ debug("%s\n", __func__);
+ rc = iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+ if (rc)
+ debug("%s: rc=%d\n", __func__, rc);
}
static ssize_t get_burstcount(struct tpm_chip *chip)
@@ -422,6 +457,8 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
if ((size_t)expected > count) {
+ error("Error size=%x, expected=%x, count=%x\n", size, expected,
+ count);
size = -EIO;
goto out;
}
@@ -456,11 +493,12 @@ out:
static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc, status;
- ssize_t burstcnt;
+ size_t burstcnt;
size_t count = 0;
int retry = 0;
u8 sts = TPM_STS_GO;
+ debug("%s: len=%d\n", __func__, len);
if (len > TPM_DEV_BUFSIZE)
return -E2BIG; /* Command is too long for our tpm, sorry */
@@ -483,9 +521,10 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
if (burstcnt < 0)
return burstcnt;
- while (count < len - 1) {
- if (burstcnt > len - 1 - count)
- burstcnt = len - 1 - count;
+ while (count < len) {
+ udelay(300);
+ if (burstcnt > len - count)
+ burstcnt = len - count;
#ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION
if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION)
@@ -497,9 +536,15 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
if (rc == 0)
count += burstcnt;
else {
- retry++;
- wait_for_stat(chip, TPM_STS_VALID,
- chip->vendor.timeout_c, &status);
+ debug("%s: error\n", __func__);
+ if (retry++ > 10) {
+ rc = -EIO;
+ goto out_err;
+ }
+ rc = wait_for_stat(chip, TPM_STS_VALID,
+ chip->vendor.timeout_c, &status);
+ if (rc)
+ goto out_err;
if ((status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO;
@@ -508,20 +553,14 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
}
}
- /* Write last byte */
- iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
- if ((status & TPM_STS_DATA_EXPECT) != 0) {
- rc = -EIO;
- goto out_err;
- }
-
/* Go and do it */
iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+ debug("done\n");
return len;
out_err:
+ debug("%s: out_err\n", __func__);
tpm_tis_i2c_ready(chip);
/*
* The TPM needs some time to clean up here,
@@ -558,26 +597,17 @@ static enum i2c_chip_type tpm_vendor_chip_type(void)
return UNKNOWN;
}
-/* Initialisation of i2c tpm */
-int tpm_vendor_init(uint32_t dev_addr)
+static int tpm_vendor_init_common(void)
{
+ struct tpm_chip *chip;
u32 vendor;
u32 expected_did_vid;
- uint old_addr;
- int rc = 0;
- struct tpm_chip *chip;
-
- old_addr = tpm_dev.addr;
- if (dev_addr != 0)
- tpm_dev.addr = dev_addr;
tpm_dev.chip_type = tpm_vendor_chip_type();
chip = tpm_register_hardware(&tpm_tis_i2c);
- if (chip < 0) {
- rc = -ENODEV;
- goto out_err;
- }
+ if (chip < 0)
+ return -ENODEV;
/* Disable interrupts (not supported) */
chip->vendor.irq = 0;
@@ -588,15 +618,13 @@ int tpm_vendor_init(uint32_t dev_addr)
chip->vendor.timeout_c = TIS_SHORT_TIMEOUT;
chip->vendor.timeout_d = TIS_SHORT_TIMEOUT;
- if (request_locality(chip, 0) < 0) {
- rc = -ENODEV;
- goto out_err;
- }
+ if (request_locality(chip, 0) < 0)
+ return -ENODEV;
/* Read four bytes from DID_VID register */
if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
- rc = -EIO;
- goto out_release;
+ release_locality(chip, 0, 1);
+ return -EIO;
}
if (tpm_dev.chip_type == SLB9635) {
@@ -609,8 +637,7 @@ int tpm_vendor_init(uint32_t dev_addr)
if (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) {
error("Vendor id did not match! ID was %08x\n", vendor);
- rc = -ENODEV;
- goto out_release;
+ return -ENODEV;
}
debug("1.2 TPM (chip type %s device-id 0x%X)\n",
@@ -622,14 +649,33 @@ int tpm_vendor_init(uint32_t dev_addr)
*/
return 0;
+}
+
+#ifdef CONFIG_DM_I2C
+/* Initialisation of i2c tpm */
+int tpm_vendor_init_dev(struct udevice *dev)
+{
+ tpm_dev.dev = dev;
+ return tpm_vendor_init_common();
+}
+#else
+/* Initialisation of i2c tpm */
+int tpm_vendor_init(uint32_t dev_addr)
+{
+ uint old_addr;
+ int rc = 0;
-out_release:
- release_locality(chip, 0, 1);
+ old_addr = tpm_dev.addr;
+ if (dev_addr != 0)
+ tpm_dev.addr = dev_addr;
+
+ rc = tpm_vendor_init_common();
+ if (rc)
+ tpm_dev.addr = old_addr;
-out_err:
- tpm_dev.addr = old_addr;
return rc;
}
+#endif
void tpm_vendor_cleanup(struct tpm_chip *chip)
{
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 3b57e56..4d35d3e 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_SL811HS) += sl811-hcd.o
obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o
obj-$(CONFIG_USB_OHCI_EP93XX) += ohci-ep93xx.o
+obj-$(CONFIG_USB_OHCI_SUNXI) += ohci-sunxi.o
# echi
obj-$(CONFIG_USB_EHCI) += ehci-hcd.o
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 86cf631..18e9251 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -25,14 +25,12 @@
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;
-#ifdef CONFIG_DM_USB
struct exynos_ehci_platdata {
struct usb_platdata usb_plat;
fdt_addr_t hcd_base;
fdt_addr_t phy_base;
struct gpio_desc vbus_gpio;
};
-#endif
/**
* Contains pointers to register base addresses
@@ -42,16 +40,8 @@ struct exynos_ehci {
struct ehci_ctrl ctrl;
struct exynos_usb_phy *usb;
struct ehci_hccr *hcd;
-#ifndef CONFIG_DM_USB
- struct gpio_desc vbus_gpio;
-#endif
};
-#ifndef CONFIG_DM_USB
-static struct exynos_ehci exynos;
-#endif
-
-#ifdef CONFIG_DM_USB
static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
{
struct exynos_ehci_platdata *plat = dev_get_platdata(dev);
@@ -91,55 +81,6 @@ static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
return 0;
}
-#else
-static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)
-{
- fdt_addr_t addr;
- unsigned int node;
- int depth;
-
- node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_EHCI);
- if (node <= 0) {
- debug("EHCI: Can't get device node for ehci\n");
- return -ENODEV;
- }
-
- /*
- * Get the base address for EHCI controller from the device node
- */
- addr = fdtdec_get_addr(blob, node, "reg");
- if (addr == FDT_ADDR_T_NONE) {
- debug("Can't get the EHCI register address\n");
- return -ENXIO;
- }
-
- exynos->hcd = (struct ehci_hccr *)addr;
-
- /* Vbus gpio */
- gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0,
- &exynos->vbus_gpio, GPIOD_IS_OUT);
-
- depth = 0;
- node = fdtdec_next_compatible_subnode(blob, node,
- COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth);
- if (node <= 0) {
- debug("EHCI: Can't get device node for usb-phy controller\n");
- return -ENODEV;
- }
-
- /*
- * Get the base address for usbphy from the device node
- */
- exynos->usb = (struct exynos_usb_phy *)fdtdec_get_addr(blob, node,
- "reg");
- if (exynos->usb == NULL) {
- debug("Can't get the usbphy register address\n");
- return -ENXIO;
- }
-
- return 0;
-}
-#endif
static void exynos5_setup_usb_phy(struct exynos_usb_phy *usb)
{
@@ -270,63 +211,6 @@ static void reset_usb_phy(struct exynos_usb_phy *usb)
set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE);
}
-#ifndef CONFIG_DM_USB
-/*
- * EHCI-initialization
- * Create the appropriate control structures to manage
- * a new EHCI host controller.
- */
-int ehci_hcd_init(int index, enum usb_init_type init,
- struct ehci_hccr **hccr, struct ehci_hcor **hcor)
-{
- struct exynos_ehci *ctx = &exynos;
-
-#ifdef CONFIG_OF_CONTROL
- if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) {
- debug("Unable to parse device tree for ehci-exynos\n");
- return -ENODEV;
- }
-#else
- ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy();
- ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci();
-#endif
-
-#ifdef CONFIG_OF_CONTROL
- /* setup the Vbus gpio here */
- if (dm_gpio_is_valid(&ctx->vbus_gpio))
- dm_gpio_set_value(&ctx->vbus_gpio, 1);
-#endif
-
- setup_usb_phy(ctx->usb);
-
- board_usb_init(index, init);
-
- *hccr = ctx->hcd;
- *hcor = (struct ehci_hcor *)((uint32_t) *hccr
- + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
-
- debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n",
- (uint32_t)*hccr, (uint32_t)*hcor,
- (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
-
- return 0;
-}
-
-/*
- * Destroy the appropriate control structures corresponding
- * the EHCI host controller.
- */
-int ehci_hcd_stop(int index)
-{
- struct exynos_ehci *ctx = &exynos;
-
- reset_usb_phy(ctx->usb);
-
- return 0;
-}
-#endif
-
-#ifdef CONFIG_DM_USB
static int ehci_usb_probe(struct udevice *dev)
{
struct exynos_ehci_platdata *plat = dev_get_platdata(dev);
@@ -377,4 +261,3 @@ U_BOOT_DRIVER(usb_ehci) = {
.platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
-#endif
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index bd9861d..1e5a6e2 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -125,14 +125,7 @@ static struct descriptor {
static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev)
{
#ifdef CONFIG_DM_USB
- struct udevice *dev;
-
- /* Find the USB controller */
- for (dev = udev->dev;
- device_get_uclass_id(dev) != UCLASS_USB;
- dev = dev->parent)
- ;
- return dev_get_priv(dev);
+ return dev_get_priv(usb_get_bus(udev->dev));
#else
return udev->controller;
#endif
@@ -310,23 +303,33 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
* in the tree before that one!
*/
#ifdef CONFIG_DM_USB
+ /*
+ * When called from usb-uclass.c: usb_scan_device() udev->dev points
+ * to the parent udevice, not the actual udevice belonging to the
+ * udev as the device is not instantiated yet. So when searching
+ * for the first usb-2 parent start with udev->dev not
+ * udev->dev->parent .
+ */
struct udevice *parent;
+ struct usb_device *uparent;
+
+ ttdev = udev;
+ parent = udev->dev;
+ uparent = dev_get_parentdata(parent);
- for (ttdev = udev; ; ) {
- struct udevice *dev = ttdev->dev;
+ while (uparent->speed != USB_SPEED_HIGH) {
+ struct udevice *dev = parent;
- if (dev->parent &&
- device_get_uclass_id(dev->parent) == UCLASS_USB_HUB)
- parent = dev->parent;
- else
- parent = NULL;
- if (!parent)
+ if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) {
+ printf("ehci: Error cannot find high speed parent of usb-1 device\n");
return;
- ttdev = dev_get_parentdata(parent);
- if (!ttdev->speed != USB_SPEED_HIGH)
- break;
+ }
+
+ ttdev = dev_get_parentdata(dev);
+ parent = dev->parent;
+ uparent = dev_get_parentdata(parent);
}
- parent_devnum = ttdev->devnum;
+ parent_devnum = uparent->devnum;
#else
ttdev = udev;
while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH)
@@ -872,7 +875,7 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe,
port - 1);
reg |= EHCI_PS_PO;
ehci_writel(status_reg, reg);
- break;
+ return -ENXIO;
} else {
int ret;
@@ -894,11 +897,22 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe,
*/
ret = handshake(status_reg, EHCI_PS_PR, 0,
2 * 1000);
- if (!ret)
- ctrl->portreset |= 1 << port;
- else
+ if (!ret) {
+ reg = ehci_readl(status_reg);
+ if ((reg & (EHCI_PS_PE | EHCI_PS_CS))
+ == EHCI_PS_CS && !ehci_is_TDI()) {
+ debug("port %d full speed --> companion\n", port - 1);
+ reg &= ~EHCI_PS_CLEAR;
+ reg |= EHCI_PS_PO;
+ ehci_writel(status_reg, reg);
+ return -ENXIO;
+ } else {
+ ctrl->portreset |= 1 << port;
+ }
+ } else {
printf("port(%d) reset error\n",
port - 1);
+ }
}
break;
case USB_PORT_FEAT_TEST:
@@ -1249,9 +1263,9 @@ disable_periodic(struct ehci_ctrl *ctrl)
return 0;
}
-struct int_queue *
-create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
- int elementsize, void *buffer, int interval)
+static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
{
struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
struct int_queue *result = NULL;
@@ -1407,7 +1421,8 @@ fail1:
return NULL;
}
-void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+static void *_ehci_poll_int_queue(struct usb_device *dev,
+ struct int_queue *queue)
{
struct QH *cur = queue->current;
struct qTD *cur_td;
@@ -1442,8 +1457,8 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
}
/* Do not free buffers associated with QHs, they're owned by someone else */
-int
-destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+static int _ehci_destroy_int_queue(struct usb_device *dev,
+ struct int_queue *queue)
{
struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
int result = -1;
@@ -1500,12 +1515,12 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
- queue = create_int_queue(dev, pipe, 1, length, buffer, interval);
+ queue = _ehci_create_int_queue(dev, pipe, 1, length, buffer, interval);
if (!queue)
return -1;
timeout = get_timer(0) + USB_TIMEOUT_MS(pipe);
- while ((backbuffer = poll_int_queue(dev, queue)) == NULL)
+ while ((backbuffer = _ehci_poll_int_queue(dev, queue)) == NULL)
if (get_timer(0) > timeout) {
printf("Timeout poll on interrupt endpoint\n");
result = -ETIMEDOUT;
@@ -1518,7 +1533,7 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
return -EINVAL;
}
- ret = destroy_int_queue(dev, queue);
+ ret = _ehci_destroy_int_queue(dev, queue);
if (ret < 0)
return ret;
@@ -1544,6 +1559,24 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe,
{
return _ehci_submit_int_msg(dev, pipe, buffer, length, interval);
}
+
+struct int_queue *create_int_queue(struct usb_device *dev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
+{
+ return _ehci_create_int_queue(dev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ehci_poll_int_queue(dev, queue);
+}
+
+int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ehci_destroy_int_queue(dev, queue);
+}
#endif
#ifdef CONFIG_DM_USB
@@ -1572,16 +1605,42 @@ static int ehci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
return _ehci_submit_int_msg(udev, pipe, buffer, length, interval);
}
+static struct int_queue *ehci_create_int_queue(struct udevice *dev,
+ struct usb_device *udev, unsigned long pipe, int queuesize,
+ int elementsize, void *buffer, int interval)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_create_int_queue(udev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+static void *ehci_poll_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_poll_int_queue(udev, queue);
+}
+
+static int ehci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_destroy_int_queue(udev, queue);
+}
+
int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
struct ehci_hcor *hcor, const struct ehci_ops *ops,
uint tweaks, enum usb_init_type init)
{
+ struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
struct ehci_ctrl *ctrl = dev_get_priv(dev);
int ret;
debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__,
dev->name, ctrl, hccr, hcor, init);
+ priv->desc_before_addr = true;
+
ehci_setup_ops(ctrl, ops);
ctrl->hccr = hccr;
ctrl->hcor = hcor;
@@ -1617,6 +1676,9 @@ struct dm_usb_ops ehci_usb_ops = {
.control = ehci_submit_control_msg,
.bulk = ehci_submit_bulk_msg,
.interrupt = ehci_submit_int_msg,
+ .create_int_queue = ehci_create_int_queue,
+ .poll_int_queue = ehci_poll_int_queue,
+ .destroy_int_queue = ehci_destroy_int_queue,
};
#endif
diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c
index 0edb643..34130f8 100644
--- a/drivers/usb/host/ehci-sunxi.c
+++ b/drivers/usb/host/ehci-sunxi.c
@@ -14,53 +14,88 @@
#include <asm/arch/clock.h>
#include <asm/arch/usb_phy.h>
#include <asm/io.h>
+#include <dm.h>
#include "ehci.h"
-int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **hccr,
- struct ehci_hcor **hcor)
+struct ehci_sunxi_priv {
+ struct ehci_ctrl ehci;
+ int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
+ int phy_index; /* Index of the usb-phy attached to this hcd */
+};
+
+static int ehci_usb_probe(struct udevice *dev)
{
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- int ahb_gate_offset;
+ struct usb_platdata *plat = dev_get_platdata(dev);
+ struct ehci_sunxi_priv *priv = dev_get_priv(dev);
+ struct ehci_hccr *hccr = (struct ehci_hccr *)dev_get_addr(dev);
+ struct ehci_hcor *hcor;
+
+ /*
+ * This should go away once we've moved to the driver model for
+ * clocks resp. phys.
+ */
+ if (hccr == (void *)SUNXI_USB1_BASE) {
+ priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0;
+ priv->phy_index = 1;
+ } else {
+ priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI1;
+ priv->phy_index = 2;
+ }
- ahb_gate_offset = index ? AHB_GATE_OFFSET_USB_EHCI1 :
- AHB_GATE_OFFSET_USB_EHCI0;
- setbits_le32(&ccm->ahb_gate0, 1 << ahb_gate_offset);
+ setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
#ifdef CONFIG_SUNXI_GEN_SUN6I
- setbits_le32(&ccm->ahb_reset0_cfg, 1 << ahb_gate_offset);
+ setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
#endif
- sunxi_usb_phy_init(index + 1);
- sunxi_usb_phy_power_on(index + 1);
-
- if (index == 0)
- *hccr = (void *)SUNXI_USB1_BASE;
- else
- *hccr = (void *)SUNXI_USB2_BASE;
-
- *hcor = (struct ehci_hcor *)((uint32_t) *hccr
- + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
+ sunxi_usb_phy_init(priv->phy_index);
+ sunxi_usb_phy_power_on(priv->phy_index);
- debug("sunxi-ehci: init hccr %x and hcor %x hc_length %d\n",
- (uint32_t)*hccr, (uint32_t)*hcor,
- (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
+ hcor = (struct ehci_hcor *)((uint32_t)hccr +
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
- return 0;
+ return ehci_register(dev, hccr, hcor, NULL, 0, plat->init_type);
}
-int ehci_hcd_stop(int index)
+static int ehci_usb_remove(struct udevice *dev)
{
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
- int ahb_gate_offset;
+ struct ehci_sunxi_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = ehci_deregister(dev);
+ if (ret)
+ return ret;
- sunxi_usb_phy_power_off(index + 1);
- sunxi_usb_phy_exit(index + 1);
+ sunxi_usb_phy_power_off(priv->phy_index);
+ sunxi_usb_phy_exit(priv->phy_index);
- ahb_gate_offset = index ? AHB_GATE_OFFSET_USB_EHCI1 :
- AHB_GATE_OFFSET_USB_EHCI0;
#ifdef CONFIG_SUNXI_GEN_SUN6I
- clrbits_le32(&ccm->ahb_reset0_cfg, 1 << ahb_gate_offset);
+ clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
#endif
- clrbits_le32(&ccm->ahb_gate0, 1 << ahb_gate_offset);
+ clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
return 0;
}
+
+static const struct udevice_id ehci_usb_ids[] = {
+ { .compatible = "allwinner,sun4i-a10-ehci", },
+ { .compatible = "allwinner,sun5i-a13-ehci", },
+ { .compatible = "allwinner,sun6i-a31-ehci", },
+ { .compatible = "allwinner,sun7i-a20-ehci", },
+ { .compatible = "allwinner,sun8i-a23-ehci", },
+ { .compatible = "allwinner,sun9i-a80-ehci", },
+ { }
+};
+
+U_BOOT_DRIVER(usb_ehci) = {
+ .name = "ehci_sunxi",
+ .id = UCLASS_USB,
+ .of_match = ehci_usb_ids,
+ .probe = ehci_usb_probe,
+ .remove = ehci_usb_remove,
+ .ops = &ehci_usb_ops,
+ .platdata_auto_alloc_size = sizeof(struct usb_platdata),
+ .priv_auto_alloc_size = sizeof(struct ehci_sunxi_priv),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 97a7ede..691ed1c 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -30,6 +30,8 @@
#include <common.h>
#include <asm/byteorder.h>
+#include <dm.h>
+#include <errno.h>
#if defined(CONFIG_PCI_OHCI)
# include <pci.h>
@@ -103,16 +105,104 @@ static struct pci_device_id ehci_pci_ids[] = {
# define m32_swap(x) cpu_to_le32(x)
#endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */
+#ifdef CONFIG_DM_USB
+/*
+ * We really should do proper cache flushing everywhere, but for now we only
+ * do it for new (driver-model) usb code to avoid regressions.
+ */
+#define flush_dcache_buffer(addr, size) \
+ flush_dcache_range((unsigned long)(addr), \
+ ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
+#define invalidate_dcache_buffer(addr, size) \
+ invalidate_dcache_range((unsigned long)(addr), \
+ ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
+#else
+#define flush_dcache_buffer(addr, size)
+#define invalidate_dcache_buffer(addr, size)
+#endif
+
+/* Do not use sizeof(ed / td) as our ed / td structs contain extra members */
+#define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16)
+#define flush_dcache_td(addr) flush_dcache_buffer(addr, 16)
+#define flush_dcache_iso_td(addr) flush_dcache_buffer(addr, 32)
+#define flush_dcache_hcca(addr) flush_dcache_buffer(addr, 256)
+#define invalidate_dcache_ed(addr) invalidate_dcache_buffer(addr, 16)
+#define invalidate_dcache_td(addr) invalidate_dcache_buffer(addr, 16)
+#define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32)
+#define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256)
+
+#ifdef CONFIG_DM_USB
+/*
+ * The various ohci_mdelay(1) calls in the code seem unnecessary. We keep
+ * them around when building for older boards not yet converted to the dm
+ * just in case (to avoid regressions), for dm this turns them into nops.
+ */
+#define ohci_mdelay(x)
+#else
+#define ohci_mdelay(x) mdelay(x)
+#endif
+
+#ifndef CONFIG_DM_USB
/* global ohci_t */
static ohci_t gohci;
/* this must be aligned to a 256 byte boundary */
struct ohci_hcca ghcca[1];
-/* a pointer to the aligned storage */
-struct ohci_hcca *phcca;
-/* this allocates EDs for all possible endpoints */
-struct ohci_device ohci_dev;
-/* device which was disconnected */
-struct usb_device *devgone;
+#endif
+
+/* mapping of the OHCI CC status to error codes */
+static int cc_to_error[16] = {
+ /* No Error */ 0,
+ /* CRC Error */ USB_ST_CRC_ERR,
+ /* Bit Stuff */ USB_ST_BIT_ERR,
+ /* Data Togg */ USB_ST_CRC_ERR,
+ /* Stall */ USB_ST_STALLED,
+ /* DevNotResp */ -1,
+ /* PIDCheck */ USB_ST_BIT_ERR,
+ /* UnExpPID */ USB_ST_BIT_ERR,
+ /* DataOver */ USB_ST_BUF_ERR,
+ /* DataUnder */ USB_ST_BUF_ERR,
+ /* reservd */ -1,
+ /* reservd */ -1,
+ /* BufferOver */ USB_ST_BUF_ERR,
+ /* BuffUnder */ USB_ST_BUF_ERR,
+ /* Not Access */ -1,
+ /* Not Access */ -1
+};
+
+static const char *cc_to_string[16] = {
+ "No Error",
+ "CRC: Last data packet from endpoint contained a CRC error.",
+ "BITSTUFFING: Last data packet from endpoint contained a bit " \
+ "stuffing violation",
+ "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \
+ "that did not match the expected value.",
+ "STALL: TD was moved to the Done Queue because the endpoint returned" \
+ " a STALL PID",
+ "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \
+ "not provide a handshake (OUT)",
+ "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\
+ "(IN) or handshake (OUT)",
+ "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \
+ "value is not defined.",
+ "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \
+ "either the size of the maximum data packet allowed\n" \
+ "from the endpoint (found in MaximumPacketSize field\n" \
+ "of ED) or the remaining buffer size.",
+ "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \
+ "and that amount was not sufficient to fill the\n" \
+ "specified buffer",
+ "reserved1",
+ "reserved2",
+ "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \
+ "than it could be written to system memory",
+ "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \
+ "system memory fast enough to keep up with data USB " \
+ "data rate.",
+ "NOT ACCESSED: This code is set by software before the TD is placed" \
+ "on a list to be processed by the HC.(1)",
+ "NOT ACCESSED: This code is set by software before the TD is placed" \
+ "on a list to be processed by the HC.(2)",
+};
static inline u32 roothub_a(struct ohci *hc)
{ return ohci_readl(&hc->regs->roothub.a); }
@@ -124,11 +214,42 @@ static inline u32 roothub_portstatus(struct ohci *hc, int i)
{ return ohci_readl(&hc->regs->roothub.portstatus[i]); }
/* forward declaration */
-static int hc_interrupt(void);
-static void td_submit_job(struct usb_device *dev, unsigned long pipe,
- void *buffer, int transfer_len,
+static int hc_interrupt(ohci_t *ohci);
+static void td_submit_job(ohci_t *ohci, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int transfer_len,
struct devrequest *setup, urb_priv_t *urb,
int interval);
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev,
+ unsigned long pipe, int interval, int load);
+
+/*-------------------------------------------------------------------------*/
+
+/* TDs ... */
+static struct td *td_alloc(ohci_dev_t *ohci_dev, struct usb_device *usb_dev)
+{
+ int i;
+ struct td *td;
+
+ td = NULL;
+ for (i = 0; i < NUM_TD; i++)
+ {
+ if (ohci_dev->tds[i].usb_dev == NULL)
+ {
+ td = &ohci_dev->tds[i];
+ td->usb_dev = usb_dev;
+ break;
+ }
+ }
+
+ return td;
+}
+
+static inline void ed_free(struct ed *ed)
+{
+ ed->usb_dev = NULL;
+}
/*-------------------------------------------------------------------------*
* URB support functions
@@ -158,18 +279,18 @@ static void urb_free_priv(urb_priv_t *urb)
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
-static int sohci_get_current_frame_number(struct usb_device *dev);
+static int sohci_get_current_frame_number(ohci_t *ohci);
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header */
-static void pkt_print(urb_priv_t *purb, struct usb_device *dev,
+static void pkt_print(ohci_t *ohci, urb_priv_t *purb, struct usb_device *dev,
unsigned long pipe, void *buffer, int transfer_len,
struct devrequest *setup, char *str, int small)
{
dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx",
str,
- sohci_get_current_frame_number(dev),
+ sohci_get_current_frame_number(ohci),
usb_pipedevice(pipe),
usb_pipeendpoint(pipe),
usb_pipeout(pipe)? 'O': 'I',
@@ -213,9 +334,11 @@ void ep_print_int_eds(ohci_t *ohci, char *str)
ed_p = &(ohci->hcca->int_table [i]);
if (*ed_p == 0)
continue;
+ invalidate_dcache_ed(ed_p);
printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i);
while (*ed_p != 0 && j--) {
ed_t *ed = (ed_t *)m32_swap(ed_p);
+ invalidate_dcache_ed(ed);
printf(" ed: %4x;", ed->hwINFO);
ed_p = &ed->hwNextED;
}
@@ -246,6 +369,7 @@ static void maybe_print_eds(char *label, __u32 value)
if (value) {
dbg("%s %08x", label, value);
+ invalidate_dcache_ed(edp);
dbg("%08x", edp->hwINFO);
dbg("%08x", edp->hwTailP);
dbg("%08x", edp->hwHeadP);
@@ -380,6 +504,7 @@ static void ohci_dump(ohci_t *controller, int verbose)
ohci_dump_status(controller);
if (verbose)
ep_print_int_eds(controller, "hcca");
+ invalidate_dcache_hcca(controller->hcca);
dbg("hcca frame #%04x", controller->hcca->frame_no);
ohci_dump_roothub(controller, 1);
}
@@ -391,9 +516,9 @@ static void ohci_dump(ohci_t *controller, int verbose)
/* get a transfer request */
-int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
+int sohci_submit_job(ohci_t *ohci, ohci_dev_t *ohci_dev, urb_priv_t *urb,
+ struct devrequest *setup)
{
- ohci_t *ohci;
ed_t *ed;
urb_priv_t *purb_priv = urb;
int i, size = 0;
@@ -403,8 +528,6 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
int transfer_len = urb->transfer_buffer_length;
int interval = urb->interval;
- ohci = &gohci;
-
/* when controller's hung, permit only roothub cleanup attempts
* such as powering down ports */
if (ohci->disabled) {
@@ -417,7 +540,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
urb->finished = 0;
/* every endpoint has a ed, locate and fill it */
- ed = ep_add_ed(dev, pipe, interval, 1);
+ ed = ep_add_ed(ohci_dev, dev, pipe, interval, 1);
if (!ed) {
err("sohci_submit_job: ENOMEM");
return -1;
@@ -453,7 +576,7 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
/* allocate the TDs */
/* note that td[0] was allocated in ep_add_ed */
for (i = 0; i < size; i++) {
- purb_priv->td[i] = td_alloc(dev);
+ purb_priv->td[i] = td_alloc(ohci_dev, dev);
if (!purb_priv->td[i]) {
purb_priv->length = i;
urb_free_priv(purb_priv);
@@ -473,55 +596,19 @@ int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
ep_link(ohci, ed);
/* fill the TDs and link it to the ed */
- td_submit_job(dev, pipe, buffer, transfer_len,
+ td_submit_job(ohci, dev, pipe, buffer, transfer_len,
setup, purb_priv, interval);
return 0;
}
-static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb)
-{
- struct ohci_regs *regs = hc->regs;
-
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_INTERRUPT:
- /* implicitly requeued */
- if (urb->dev->irq_handle &&
- (urb->dev->irq_act_len = urb->actual_length)) {
- ohci_writel(OHCI_INTR_WDH, &regs->intrenable);
- ohci_readl(&regs->intrenable); /* PCI posting flush */
- urb->dev->irq_handle(urb->dev);
- ohci_writel(OHCI_INTR_WDH, &regs->intrdisable);
- ohci_readl(&regs->intrdisable); /* PCI posting flush */
- }
- urb->actual_length = 0;
- td_submit_job(
- urb->dev,
- urb->pipe,
- urb->transfer_buffer,
- urb->transfer_buffer_length,
- NULL,
- urb,
- urb->interval);
- break;
- case PIPE_CONTROL:
- case PIPE_BULK:
- break;
- default:
- return 0;
- }
- return 1;
-}
-
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
/* tell us the current USB frame number */
-
-static int sohci_get_current_frame_number(struct usb_device *usb_dev)
+static int sohci_get_current_frame_number(ohci_t *ohci)
{
- ohci_t *ohci = &gohci;
-
+ invalidate_dcache_hcca(ohci->hcca);
return m16_swap(ohci->hcca->frame_no);
}
#endif
@@ -600,6 +687,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
switch (ed->type) {
case PIPE_CONTROL:
ed->hwNextED = 0;
+ flush_dcache_ed(ed);
if (ohci->ed_controltail == NULL)
ohci_writel(ed, &ohci->regs->ed_controlhead);
else
@@ -617,6 +705,7 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
case PIPE_BULK:
ed->hwNextED = 0;
+ flush_dcache_ed(ed);
if (ohci->ed_bulktail == NULL)
ohci_writel(ed, &ohci->regs->ed_bulkhead);
else
@@ -649,7 +738,9 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
inter = ep_rev(6,
((ed_t *)ed_p)->int_interval);
ed->hwNextED = *ed_p;
+ flush_dcache_ed(ed);
*ed_p = m32_swap((unsigned long)ed);
+ flush_dcache_hcca(ohci->hcca);
}
break;
}
@@ -662,6 +753,8 @@ static int ep_link(ohci_t *ohci, ed_t *edi)
static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed,
unsigned index, unsigned period)
{
+ __maybe_unused unsigned long aligned_ed_p;
+
for (; index < NUM_INTS; index += period) {
__u32 *ed_p = &ohci->hcca->int_table [index];
@@ -670,6 +763,12 @@ static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed,
if (((struct ed *)
m32_swap((unsigned long)ed_p)) == ed) {
*ed_p = ed->hwNextED;
+#ifdef CONFIG_DM_USB
+ aligned_ed_p = (unsigned long)ed_p;
+ aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1);
+ flush_dcache_range(aligned_ed_p,
+ aligned_ed_p + ARCH_DMA_MINALIGN);
+#endif
break;
}
ed_p = &(((struct ed *)
@@ -689,6 +788,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
int i;
ed->hwINFO |= m32_swap(OHCI_ED_SKIP);
+ flush_dcache_ed(ed);
switch (ed->type) {
case PIPE_CONTROL:
@@ -702,6 +802,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
&ohci->regs->ed_controlhead);
} else {
ed->ed_prev->hwNextED = ed->hwNextED;
+ flush_dcache_ed(ed->ed_prev);
}
if (ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
@@ -722,6 +823,7 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
&ohci->regs->ed_bulkhead);
} else {
ed->ed_prev->hwNextED = ed->hwNextED;
+ flush_dcache_ed(ed->ed_prev);
}
if (ohci->ed_bulktail == ed) {
ohci->ed_bulktail = ed->ed_prev;
@@ -751,14 +853,14 @@ static int ep_unlink(ohci_t *ohci, ed_t *edi)
* info fields are setted anyway even though most of them should not
* change
*/
-static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe,
- int interval, int load)
+static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev,
+ unsigned long pipe, int interval, int load)
{
td_t *td;
ed_t *ed_ret;
volatile ed_t *ed;
- ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) |
+ ed = ed_ret = &ohci_dev->ed[(usb_pipeendpoint(pipe) << 1) |
(usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))];
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
@@ -769,12 +871,12 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe,
if (ed->state == ED_NEW) {
/* dummy td; end of td list for ed */
- td = td_alloc(usb_dev);
+ td = td_alloc(ohci_dev, usb_dev);
ed->hwTailP = m32_swap((unsigned long)td);
ed->hwHeadP = ed->hwTailP;
ed->state = ED_UNLINK;
ed->type = usb_pipetype(pipe);
- ohci_dev.ed_cnt++;
+ ohci_dev->ed_cnt++;
}
ed->hwINFO = m32_swap(usb_pipedevice(pipe)
@@ -790,6 +892,8 @@ static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe,
ed->int_load = load;
}
+ flush_dcache_ed(ed);
+
return ed_ret;
}
@@ -815,6 +919,7 @@ static void td_fill(ohci_t *ohci, unsigned int info,
/* use this td as the next dummy */
td_pt = urb_priv->td [index];
td_pt->hwNextTD = 0;
+ flush_dcache_td(td_pt);
/* fill the old dummy TD */
td = urb_priv->td [index] =
@@ -842,27 +947,30 @@ static void td_fill(ohci_t *ohci, unsigned int info,
td->hwBE = 0;
td->hwNextTD = m32_swap((unsigned long)td_pt);
+ flush_dcache_td(td);
/* append to queue */
td->ed->hwTailP = td->hwNextTD;
+ flush_dcache_ed(td->ed);
}
/*-------------------------------------------------------------------------*/
/* prepare all TDs of a transfer */
-static void td_submit_job(struct usb_device *dev, unsigned long pipe,
- void *buffer, int transfer_len,
+static void td_submit_job(ohci_t *ohci, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int transfer_len,
struct devrequest *setup, urb_priv_t *urb,
int interval)
{
- ohci_t *ohci = &gohci;
int data_len = transfer_len;
void *data;
int cnt = 0;
__u32 info = 0;
unsigned int toggle = 0;
+ flush_dcache_buffer(buffer, data_len);
+
/* OHCI handles the DATA-toggles itself, we just use the USB-toggle
* bits for reseting */
if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
@@ -902,6 +1010,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe,
case PIPE_CONTROL:
/* Setup phase */
info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ flush_dcache_buffer(setup, 8);
td_fill(ohci, info, setup, 8, dev, cnt++, urb);
/* Optional Data phase */
@@ -914,7 +1023,7 @@ static void td_submit_job(struct usb_device *dev, unsigned long pipe,
}
/* Status phase */
- info = usb_pipeout(pipe)?
+ info = (usb_pipeout(pipe) || data_len == 0) ?
TD_CC | TD_DP_IN | TD_T_DATA1:
TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill(ohci, info, data, 0, dev, cnt++, urb);
@@ -973,6 +1082,7 @@ static void check_status(td_t *td_list)
if (cc) {
err(" USB-error: %s (%x)", cc_to_string[cc], cc);
+ invalidate_dcache_ed(td_list->ed);
if (*phwHeadP & m32_swap(0x1)) {
if (lurb_priv &&
((td_list->index + 1) < urb_len)) {
@@ -985,9 +1095,11 @@ static void check_status(td_t *td_list)
td_list->index - 1;
} else
*phwHeadP &= m32_swap(0xfffffff2);
+ flush_dcache_ed(td_list->ed);
}
#ifdef CONFIG_MPC5200
td_list->hwNextTD = 0;
+ flush_dcache_td(td_list);
#endif
}
}
@@ -1000,11 +1112,14 @@ static td_t *dl_reverse_done_list(ohci_t *ohci)
td_t *td_rev = NULL;
td_t *td_list = NULL;
+ invalidate_dcache_hcca(ohci->hcca);
td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0;
ohci->hcca->done_head = 0;
+ flush_dcache_hcca(ohci->hcca);
while (td_list_hc) {
td_list = (td_t *)td_list_hc;
+ invalidate_dcache_td(td_list);
check_status(td_list);
td_list->next_dl_td = td_rev;
td_rev = td_list;
@@ -1019,7 +1134,7 @@ static td_t *dl_reverse_done_list(ohci_t *ohci)
static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status)
{
if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL))
- urb->finished = sohci_return_job(ohci, urb);
+ urb->finished = 1;
else
dbg("finish_urb: strange.., ED state %x, \n", status);
}
@@ -1039,6 +1154,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list)
urb_priv_t *lurb_priv;
__u32 tdINFO, edHeadP, edTailP;
+ invalidate_dcache_td(td_list);
tdINFO = m32_swap(td_list->hwINFO);
ed = td_list->ed;
@@ -1064,6 +1180,7 @@ static int takeback_td(ohci_t *ohci, td_t *td_list)
lurb_priv->td_cnt, lurb_priv->length);
if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) {
+ invalidate_dcache_ed(ed);
edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0;
edTailP = m32_swap(ed->hwTailP);
@@ -1100,16 +1217,16 @@ static int dl_done_list(ohci_t *ohci)
#define OK(x) len = (x); break
#ifdef DEBUG
#define WR_RH_STAT(x) {info("WR:status %#8x", (x)); ohci_writel((x), \
- &gohci.regs->roothub.status); }
+ &ohci->regs->roothub.status); }
#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \
- (x)); ohci_writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); }
+ (x)); ohci_writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); }
#else
-#define WR_RH_STAT(x) ohci_writel((x), &gohci.regs->roothub.status)
+#define WR_RH_STAT(x) ohci_writel((x), &ohci->regs->roothub.status)
#define WR_RH_PORTSTAT(x) ohci_writel((x), \
- &gohci.regs->roothub.portstatus[wIndex-1])
+ &ohci->regs->roothub.portstatus[wIndex-1])
#endif
-#define RD_RH_STAT roothub_status(&gohci)
-#define RD_RH_PORTSTAT roothub_portstatus(&gohci, wIndex-1)
+#define RD_RH_STAT roothub_status(ohci)
+#define RD_RH_PORTSTAT roothub_portstatus(ohci, wIndex-1)
/* request to virtual root hub */
@@ -1137,8 +1254,9 @@ int rh_check_port_status(ohci_t *controller)
return res;
}
-static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
- void *buffer, int transfer_len, struct devrequest *cmd)
+static int ohci_submit_rh_msg(ohci_t *ohci, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int transfer_len,
+ struct devrequest *cmd)
{
void *data = buffer;
int leni = transfer_len;
@@ -1151,10 +1269,10 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32));
#ifdef DEBUG
-pkt_print(NULL, dev, pipe, buffer, transfer_len,
+pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len,
cmd, "SUB(rh)", usb_pipein(pipe));
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
if (usb_pipeint(pipe)) {
info("Root-Hub submit IRQ: NOT implemented");
@@ -1235,7 +1353,6 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
OK(0);
case (RH_PORT_POWER):
WR_RH_PORTSTAT(RH_PS_PPS);
- mdelay(100);
OK(0);
case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
if (RD_RH_PORTSTAT & RH_PS_CCS)
@@ -1245,7 +1362,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
break;
case RH_SET_ADDRESS:
- gohci.rh.devnum = wValue;
+ ohci->rh.devnum = wValue;
OK(0);
case RH_GET_DESCRIPTOR:
@@ -1290,7 +1407,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
case RH_GET_DESCRIPTOR | RH_CLASS:
{
- __u32 temp = roothub_a(&gohci);
+ __u32 temp = roothub_a(ohci);
databuf[0] = 9; /* min length; */
databuf[1] = 0x29;
@@ -1309,7 +1426,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
databuf[4] = 0;
databuf[5] = (temp & RH_A_POTPGT) >> 24;
databuf[6] = 0;
- temp = roothub_b(&gohci);
+ temp = roothub_b(ohci);
databuf[7] = temp & RH_B_DR;
if (databuf[2] < 7) {
databuf[8] = 0xff;
@@ -1338,9 +1455,9 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
}
#ifdef DEBUG
- ohci_dump_roothub(&gohci, 1);
+ ohci_dump_roothub(ohci, 1);
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
len = min_t(int, len, leni);
@@ -1350,10 +1467,10 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
dev->status = stat;
#ifdef DEBUG
- pkt_print(NULL, dev, pipe, buffer,
+ pkt_print(ohci, NULL, dev, pipe, buffer,
transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/);
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
return stat;
@@ -1361,18 +1478,43 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
/*-------------------------------------------------------------------------*/
+static ohci_dev_t *ohci_get_ohci_dev(ohci_t *ohci, int devnum, int intr)
+{
+ int i;
+
+ if (!intr)
+ return &ohci->ohci_dev;
+
+ /* First see if we already have an ohci_dev for this dev. */
+ for (i = 0; i < NUM_INT_DEVS; i++) {
+ if (ohci->int_dev[i].devnum == devnum)
+ return &ohci->int_dev[i];
+ }
+
+ /* If not then find a free one. */
+ for (i = 0; i < NUM_INT_DEVS; i++) {
+ if (ohci->int_dev[i].devnum == -1) {
+ ohci->int_dev[i].devnum = devnum;
+ return &ohci->int_dev[i];
+ }
+ }
+
+ printf("ohci: Error out of ohci_devs for interrupt endpoints\n");
+ return NULL;
+}
+
/* common code for handling submit messages - used for all but root hub */
/* accesses. */
-int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int transfer_len, struct devrequest *setup, int interval)
+static urb_priv_t *ohci_alloc_urb(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int transfer_len, int interval)
{
- int stat = 0;
- int maxsize = usb_maxpacket(dev, pipe);
- int timeout;
urb_priv_t *urb;
- urb = malloc(sizeof(urb_priv_t));
- memset(urb, 0, sizeof(urb_priv_t));
+ urb = calloc(1, sizeof(urb_priv_t));
+ if (!urb) {
+ printf("ohci: Error out of memory allocating urb\n");
+ return NULL;
+ }
urb->dev = dev;
urb->pipe = pipe;
@@ -1380,18 +1522,29 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
urb->transfer_buffer_length = transfer_len;
urb->interval = interval;
- /* device pulled? Shortcut the action. */
- if (devgone == dev) {
- dev->status = USB_ST_CRC_ERR;
- return 0;
- }
+ return urb;
+}
+
+static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int transfer_len,
+ struct devrequest *setup, int interval)
+{
+ int stat = 0;
+ int maxsize = usb_maxpacket(dev, pipe);
+ int timeout;
+ urb_priv_t *urb;
+ ohci_dev_t *ohci_dev;
+
+ urb = ohci_alloc_urb(dev, pipe, buffer, transfer_len, interval);
+ if (!urb)
+ return -ENOMEM;
#ifdef DEBUG
urb->actual_length = 0;
- pkt_print(urb, dev, pipe, buffer, transfer_len,
+ pkt_print(ohci, urb, dev, pipe, buffer, transfer_len,
setup, "SUB", usb_pipein(pipe));
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
if (!maxsize) {
err("submit_common_message: pipesize for pipe %lx is zero",
@@ -1399,14 +1552,18 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
return -1;
}
- if (sohci_submit_job(urb, setup) < 0) {
+ ohci_dev = ohci_get_ohci_dev(ohci, dev->devnum, usb_pipeint(pipe));
+ if (!ohci_dev)
+ return -ENOMEM;
+
+ if (sohci_submit_job(ohci, ohci_dev, urb, setup) < 0) {
err("sohci_submit_job failed");
return -1;
}
#if 0
mdelay(10);
- /* ohci_dump_status(&gohci); */
+ /* ohci_dump_status(ohci); */
#endif
timeout = USB_TIMEOUT_MS(pipe);
@@ -1414,7 +1571,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
/* wait for it to complete */
for (;;) {
/* check whether the controller is done */
- stat = hc_interrupt();
+ stat = hc_interrupt(ohci);
if (stat < 0) {
stat = USB_ST_CRC_ERR;
break;
@@ -1440,7 +1597,8 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
dbg("*");
} else {
- err("CTL:TIMEOUT ");
+ if (!usb_pipeint(pipe))
+ err("CTL:TIMEOUT ");
dbg("submit_common_msg: TO status %x\n", stat);
urb->finished = 1;
stat = USB_ST_CRC_ERR;
@@ -1451,60 +1609,168 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
dev->status = stat;
dev->act_len = urb->actual_length;
+ if (usb_pipein(pipe) && dev->status == 0 && dev->act_len)
+ invalidate_dcache_buffer(buffer, dev->act_len);
+
#ifdef DEBUG
- pkt_print(urb, dev, pipe, buffer, transfer_len,
+ pkt_print(ohci, urb, dev, pipe, buffer, transfer_len,
setup, "RET(ctlr)", usb_pipein(pipe));
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
+ urb_free_priv(urb);
+ return 0;
+}
+
+#define MAX_INT_QUEUESIZE 8
+
+struct int_queue {
+ int queuesize;
+ int curr_urb;
+ urb_priv_t *urb[MAX_INT_QUEUESIZE];
+};
+
+static struct int_queue *_ohci_create_int_queue(ohci_t *ohci,
+ struct usb_device *udev, unsigned long pipe, int queuesize,
+ int elementsize, void *buffer, int interval)
+{
+ struct int_queue *queue;
+ ohci_dev_t *ohci_dev;
+ int i;
+
+ if (queuesize > MAX_INT_QUEUESIZE)
+ return NULL;
+
+ ohci_dev = ohci_get_ohci_dev(ohci, udev->devnum, 1);
+ if (!ohci_dev)
+ return NULL;
+
+ queue = malloc(sizeof(*queue));
+ if (!queue) {
+ printf("ohci: Error out of memory allocating int queue\n");
+ return NULL;
+ }
+
+ for (i = 0; i < queuesize; i++) {
+ queue->urb[i] = ohci_alloc_urb(udev, pipe,
+ buffer + i * elementsize,
+ elementsize, interval);
+ if (!queue->urb[i])
+ break;
+
+ if (sohci_submit_job(ohci, ohci_dev, queue->urb[i], NULL)) {
+ printf("ohci: Error submitting int queue job\n");
+ urb_free_priv(queue->urb[i]);
+ break;
+ }
+ }
+ if (i == 0) {
+ /* We did not succeed in submitting even 1 urb */
+ free(queue);
+ return NULL;
+ }
+
+ queue->queuesize = i;
+ queue->curr_urb = 0;
+
+ return queue;
+}
+
+static void *_ohci_poll_int_queue(ohci_t *ohci, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ if (queue->curr_urb == queue->queuesize)
+ return NULL; /* Queue depleted */
+
+ if (hc_interrupt(ohci) < 0)
+ return NULL;
+
+ if (queue->urb[queue->curr_urb]->finished) {
+ void *ret = queue->urb[queue->curr_urb]->transfer_buffer;
+ queue->curr_urb++;
+ return ret;
+ }
+
+ return NULL;
+}
+
+static int _ohci_destroy_int_queue(ohci_t *ohci, struct usb_device *dev,
+ struct int_queue *queue)
+{
+ int i;
+
+ for (i = 0; i < queue->queuesize; i++)
+ urb_free_priv(queue->urb[i]);
+
+ free(queue);
- /* free TDs in urb_priv */
- if (!usb_pipeint(pipe))
- urb_free_priv(urb);
return 0;
}
+#ifndef CONFIG_DM_USB
/* submit routines called from usb.c */
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len)
{
info("submit_bulk_msg");
- return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
+ return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len,
+ NULL, 0);
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, int interval)
+{
+ info("submit_int_msg");
+ return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, NULL,
+ interval);
+}
+
+struct int_queue *create_int_queue(struct usb_device *dev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
+{
+ return _ohci_create_int_queue(&gohci, dev, pipe, queuesize,
+ elementsize, buffer, interval);
}
-int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int transfer_len, struct devrequest *setup)
+void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ohci_poll_int_queue(&gohci, dev, queue);
+}
+
+int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ohci_destroy_int_queue(&gohci, dev, queue);
+}
+#endif
+
+static int _ohci_submit_control_msg(ohci_t *ohci, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int transfer_len,
+ struct devrequest *setup)
{
int maxsize = usb_maxpacket(dev, pipe);
info("submit_control_msg");
#ifdef DEBUG
- pkt_print(NULL, dev, pipe, buffer, transfer_len,
+ pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len,
setup, "SUB", usb_pipein(pipe));
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
if (!maxsize) {
err("submit_control_message: pipesize for pipe %lx is zero",
pipe);
return -1;
}
- if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
- gohci.rh.dev = dev;
+ if (((pipe >> 8) & 0x7f) == ohci->rh.devnum) {
+ ohci->rh.dev = dev;
/* root hub - redirect */
- return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
- setup);
+ return ohci_submit_rh_msg(ohci, dev, pipe, buffer,
+ transfer_len, setup);
}
- return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
-}
-
-int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int transfer_len, int interval)
-{
- info("submit_int_msg");
- return submit_common_msg(dev, pipe, buffer, transfer_len, NULL,
- interval);
+ return submit_common_msg(ohci, dev, pipe, buffer, transfer_len,
+ setup, 0);
}
/*-------------------------------------------------------------------------*
@@ -1593,8 +1859,11 @@ static int hc_start(ohci_t *ohci)
{
__u32 mask;
unsigned int fminterval;
+ int i;
ohci->disabled = 1;
+ for (i = 0; i < NUM_INT_DEVS; i++)
+ ohci->int_dev[i].devnum = -1;
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
@@ -1635,9 +1904,6 @@ static int hc_start(ohci_t *ohci)
ohci_writel(RH_HS_LPSC, &ohci->regs->roothub.status);
#endif /* OHCI_USE_NPS */
- /* POTPGT delay is bits 24-31, in 2 ms units. */
- mdelay((roothub_a(ohci) >> 23) & 0x1fe);
-
/* connect the virtual root hub */
ohci->rh.devnum = 0;
@@ -1648,13 +1914,14 @@ static int hc_start(ohci_t *ohci)
/* an interrupt happens */
-static int hc_interrupt(void)
+static int hc_interrupt(ohci_t *ohci)
{
- ohci_t *ohci = &gohci;
struct ohci_regs *regs = ohci->regs;
int ints;
int stat = -1;
+ invalidate_dcache_hcca(ohci->hcca);
+
if ((ohci->hcca->done_head != 0) &&
!(m32_swap(ohci->hcca->done_head) & 0x01)) {
ints = OHCI_INTR_WDH;
@@ -1688,7 +1955,7 @@ static int hc_interrupt(void)
#ifdef DEBUG
ohci_dump(ohci, 1);
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
/* FIXME: be optimistic, hope that bug won't repeat often. */
/* Make some non-interrupt context restart the controller. */
@@ -1699,10 +1966,10 @@ static int hc_interrupt(void)
}
if (ints & OHCI_INTR_WDH) {
- mdelay(1);
+ ohci_mdelay(1);
ohci_writel(OHCI_INTR_WDH, &regs->intrdisable);
(void)ohci_readl(&regs->intrdisable); /* flush */
- stat = dl_done_list(&gohci);
+ stat = dl_done_list(ohci);
ohci_writel(OHCI_INTR_WDH, &regs->intrenable);
(void)ohci_readl(&regs->intrdisable); /* flush */
}
@@ -1729,6 +1996,8 @@ static int hc_interrupt(void)
/*-------------------------------------------------------------------------*/
+#ifndef CONFIG_DM_USB
+
/*-------------------------------------------------------------------------*/
/* De-allocate all resources.. */
@@ -1772,21 +2041,9 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
err("HCCA not aligned!!");
return -1;
}
- phcca = &ghcca[0];
- info("aligned ghcca %p", phcca);
- memset(&ohci_dev, 0, sizeof(struct ohci_device));
- if ((__u32)&ohci_dev.ed[0] & 0x7) {
- err("EDs not aligned!!");
- return -1;
- }
- memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
- if ((__u32)gtd & 0x7) {
- err("TDs not aligned!!");
- return -1;
- }
- ptd = gtd;
- gohci.hcca = phcca;
- memset(phcca, 0, sizeof(struct ohci_hcca));
+ gohci.hcca = &ghcca[0];
+ info("aligned ghcca %p", gohci.hcca);
+ memset(gohci.hcca, 0, sizeof(struct ohci_hcca));
gohci.disabled = 1;
gohci.sleeping = 0;
@@ -1848,7 +2105,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
#ifdef DEBUG
ohci_dump(&gohci, 1);
#else
- mdelay(1);
+ ohci_mdelay(1);
#endif
ohci_inited = 1;
return 0;
@@ -1880,3 +2137,115 @@ int usb_lowlevel_stop(int index)
ohci_inited = 0;
return 0;
}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int transfer_len, struct devrequest *setup)
+{
+ return _ohci_submit_control_msg(&gohci, dev, pipe, buffer,
+ transfer_len, setup);
+}
+#endif
+
+#ifdef CONFIG_DM_USB
+static int ohci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ struct devrequest *setup)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_submit_control_msg(ohci, udev, pipe, buffer,
+ length, setup);
+}
+
+static int ohci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return submit_common_msg(ohci, udev, pipe, buffer, length, NULL, 0);
+}
+
+static int ohci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ int interval)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return submit_common_msg(ohci, udev, pipe, buffer, length,
+ NULL, interval);
+}
+
+static struct int_queue *ohci_create_int_queue(struct udevice *dev,
+ struct usb_device *udev, unsigned long pipe, int queuesize,
+ int elementsize, void *buffer, int interval)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_create_int_queue(ohci, udev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+static void *ohci_poll_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_poll_int_queue(ohci, udev, queue);
+}
+
+static int ohci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_destroy_int_queue(ohci, udev, queue);
+}
+
+int ohci_register(struct udevice *dev, struct ohci_regs *regs)
+{
+ struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+ ohci_t *ohci = dev_get_priv(dev);
+ u32 reg;
+
+ priv->desc_before_addr = true;
+
+ ohci->regs = regs;
+ ohci->hcca = memalign(256, sizeof(struct ohci_hcca));
+ if (!ohci->hcca)
+ return -ENOMEM;
+ memset(ohci->hcca, 0, sizeof(struct ohci_hcca));
+
+ if (hc_reset(ohci) < 0)
+ return -EIO;
+
+ if (hc_start(ohci) < 0)
+ return -EIO;
+
+ reg = ohci_readl(&regs->revision);
+ printf("USB OHCI %x.%x\n", (reg >> 4) & 0xf, reg & 0xf);
+
+ return 0;
+}
+
+int ohci_deregister(struct udevice *dev)
+{
+ ohci_t *ohci = dev_get_priv(dev);
+
+ if (hc_reset(ohci) < 0)
+ return -EIO;
+
+ free(ohci->hcca);
+
+ return 0;
+}
+
+struct dm_usb_ops ohci_usb_ops = {
+ .control = ohci_submit_control_msg,
+ .bulk = ohci_submit_bulk_msg,
+ .interrupt = ohci_submit_int_msg,
+ .create_int_queue = ohci_create_int_queue,
+ .poll_int_queue = ohci_poll_int_queue,
+ .destroy_int_queue = ohci_destroy_int_queue,
+};
+
+#endif
diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c
new file mode 100644
index 0000000..e33a8f7
--- /dev/null
+++ b/drivers/usb/host/ohci-sunxi.c
@@ -0,0 +1,104 @@
+/*
+ * Sunxi ohci glue
+ *
+ * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/usb_phy.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <usb.h>
+#include "ohci.h"
+
+struct ohci_sunxi_priv {
+ ohci_t ohci;
+ int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
+ int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */
+ int phy_index; /* Index of the usb-phy attached to this hcd */
+};
+
+static int ohci_usb_probe(struct udevice *dev)
+{
+ struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
+ struct ohci_sunxi_priv *priv = dev_get_priv(dev);
+ struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev);
+
+ bus_priv->companion = true;
+
+ /*
+ * This should go away once we've moved to the driver model for
+ * clocks resp. phys.
+ */
+ if (regs == (void *)(SUNXI_USB1_BASE + 0x400)) {
+ priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
+ priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK;
+ priv->phy_index = 1;
+ } else {
+ priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI1;
+ priv->usb_gate_mask = CCM_USB_CTRL_OHCI1_CLK;
+ priv->phy_index = 2;
+ }
+
+ setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
+ setbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask);
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+ setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
+#endif
+
+ sunxi_usb_phy_init(priv->phy_index);
+ sunxi_usb_phy_power_on(priv->phy_index);
+
+ return ohci_register(dev, regs);
+}
+
+static int ohci_usb_remove(struct udevice *dev)
+{
+ struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ struct ohci_sunxi_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = ohci_deregister(dev);
+ if (ret)
+ return ret;
+
+ sunxi_usb_phy_power_off(priv->phy_index);
+ sunxi_usb_phy_exit(priv->phy_index);
+
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+ clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask);
+#endif
+ clrbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask);
+ clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask);
+
+ return 0;
+}
+
+static const struct udevice_id ohci_usb_ids[] = {
+ { .compatible = "allwinner,sun4i-a10-ohci", },
+ { .compatible = "allwinner,sun5i-a13-ohci", },
+ { .compatible = "allwinner,sun6i-a31-ohci", },
+ { .compatible = "allwinner,sun7i-a20-ohci", },
+ { .compatible = "allwinner,sun8i-a23-ohci", },
+ { .compatible = "allwinner,sun9i-a80-ohci", },
+ { }
+};
+
+U_BOOT_DRIVER(usb_ohci) = {
+ .name = "ohci_sunxi",
+ .id = UCLASS_USB,
+ .of_match = ohci_usb_ids,
+ .probe = ohci_usb_probe,
+ .remove = ohci_usb_remove,
+ .ops = &ohci_usb_ops,
+ .platdata_auto_alloc_size = sizeof(struct usb_platdata),
+ .priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv),
+ .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 9a4a2c2..f1526d4 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -18,6 +18,18 @@
# define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a))
#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */
+#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 16
+#define ED_ALIGNMENT ARCH_DMA_MINALIGN
+#else
+#define ED_ALIGNMENT 16
+#endif
+
+#if defined CONFIG_DM_USB && ARCH_DMA_MINALIGN > 32
+#define TD_ALIGNMENT ARCH_DMA_MINALIGN
+#else
+#define TD_ALIGNMENT 32
+#endif
+
/* functions for doing board or CPU specific setup/cleanup */
int usb_board_stop(void);
@@ -25,64 +37,7 @@ int usb_cpu_init(void);
int usb_cpu_stop(void);
int usb_cpu_init_fail(void);
-static int cc_to_error[16] = {
-
-/* mapping of the OHCI CC status to error codes */
- /* No Error */ 0,
- /* CRC Error */ USB_ST_CRC_ERR,
- /* Bit Stuff */ USB_ST_BIT_ERR,
- /* Data Togg */ USB_ST_CRC_ERR,
- /* Stall */ USB_ST_STALLED,
- /* DevNotResp */ -1,
- /* PIDCheck */ USB_ST_BIT_ERR,
- /* UnExpPID */ USB_ST_BIT_ERR,
- /* DataOver */ USB_ST_BUF_ERR,
- /* DataUnder */ USB_ST_BUF_ERR,
- /* reservd */ -1,
- /* reservd */ -1,
- /* BufferOver */ USB_ST_BUF_ERR,
- /* BuffUnder */ USB_ST_BUF_ERR,
- /* Not Access */ -1,
- /* Not Access */ -1
-};
-
-static const char *cc_to_string[16] = {
- "No Error",
- "CRC: Last data packet from endpoint contained a CRC error.",
- "BITSTUFFING: Last data packet from endpoint contained a bit " \
- "stuffing violation",
- "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \
- "that did not match the expected value.",
- "STALL: TD was moved to the Done Queue because the endpoint returned" \
- " a STALL PID",
- "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \
- "not provide a handshake (OUT)",
- "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\
- "(IN) or handshake (OUT)",
- "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \
- "value is not defined.",
- "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \
- "either the size of the maximum data packet allowed\n" \
- "from the endpoint (found in MaximumPacketSize field\n" \
- "of ED) or the remaining buffer size.",
- "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \
- "and that amount was not sufficient to fill the\n" \
- "specified buffer",
- "reserved1",
- "reserved2",
- "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \
- "than it could be written to system memory",
- "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \
- "system memory fast enough to keep up with data USB " \
- "data rate.",
- "NOT ACCESSED: This code is set by software before the TD is placed" \
- "on a list to be processed by the HC.(1)",
- "NOT ACCESSED: This code is set by software before the TD is placed" \
- "on a list to be processed by the HC.(2)",
-};
-
/* ED States */
-
#define ED_NEW 0x00
#define ED_UNLINK 0x01
#define ED_OPER 0x02
@@ -109,7 +64,7 @@ struct ed {
struct usb_device *usb_dev;
void *purb;
__u32 unused[2];
-} __attribute__((aligned(16)));
+} __attribute__((aligned(ED_ALIGNMENT)));
typedef struct ed ed_t;
@@ -169,7 +124,7 @@ struct td {
__u32 data;
__u32 unused2[2];
-} __attribute__((aligned(32)));
+} __attribute__((aligned(TD_ALIGNMENT)));
typedef struct td td_t;
#define OHCI_ED_SKIP (1 << 14)
@@ -408,6 +363,19 @@ typedef struct
} urb_priv_t;
#define URB_DEL 1
+#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
+
+#define NUM_TD 64 /* we need more TDs than EDs */
+
+#define NUM_INT_DEVS 8 /* num of ohci_dev structs for int endpoints */
+
+typedef struct ohci_device {
+ ed_t ed[NUM_EDS] __aligned(ED_ALIGNMENT);
+ td_t tds[NUM_TD] __aligned(TD_ALIGNMENT);
+ int ed_cnt;
+ int devnum;
+} ohci_dev_t;
+
/*
* This is the full ohci controller description
*
@@ -417,6 +385,9 @@ typedef struct
typedef struct ohci {
+ /* this allocates EDs for all possible endpoints */
+ struct ohci_device ohci_dev __aligned(TD_ALIGNMENT);
+ struct ohci_device int_dev[NUM_INT_DEVS] __aligned(TD_ALIGNMENT);
struct ohci_hcca *hcca; /* hcca */
/*dma_addr_t hcca_dma;*/
@@ -439,53 +410,9 @@ typedef struct ohci {
const char *slot_name;
} ohci_t;
-#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
-
-struct ohci_device {
- ed_t ed[NUM_EDS];
- int ed_cnt;
-};
-
-/* hcd */
-/* endpoint */
-static int ep_link(ohci_t * ohci, ed_t * ed);
-static int ep_unlink(ohci_t * ohci, ed_t * ed);
-static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe,
- int interval, int load);
-
-/*-------------------------------------------------------------------------*/
-
-/* we need more TDs than EDs */
-#define NUM_TD 64
-
-/* +1 so we can align the storage */
-td_t gtd[NUM_TD+1];
-/* pointers to aligned storage */
-td_t *ptd;
+#ifdef CONFIG_DM_USB
+extern struct dm_usb_ops ohci_usb_ops;
-/* TDs ... */
-static inline struct td *
-td_alloc (struct usb_device *usb_dev)
-{
- int i;
- struct td *td;
-
- td = NULL;
- for (i = 0; i < NUM_TD; i++)
- {
- if (ptd[i].usb_dev == NULL)
- {
- td = &ptd[i];
- td->usb_dev = usb_dev;
- break;
- }
- }
-
- return td;
-}
-
-static inline void
-ed_free (struct ed *ed)
-{
- ed->usb_dev = NULL;
-}
+int ohci_register(struct udevice *dev, struct ohci_regs *regs);
+int ohci_deregister(struct udevice *dev);
+#endif
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index 714bc0e..963464c 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -21,6 +21,10 @@ DECLARE_GLOBAL_DATA_PTR;
extern bool usb_started; /* flag for the started/stopped USB status */
static bool asynch_allowed;
+struct usb_uclass_priv {
+ int companion_device_count;
+};
+
int usb_disable_asynch(int disable)
{
int old_value = asynch_allowed;
@@ -46,11 +50,22 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe,
{
struct udevice *bus = udev->controller_dev;
struct dm_usb_ops *ops = usb_get_ops(bus);
+ struct usb_uclass_priv *uc_priv = bus->uclass->priv;
+ int err;
if (!ops->control)
return -ENOSYS;
- return ops->control(bus, udev, pipe, buffer, length, setup);
+ err = ops->control(bus, udev, pipe, buffer, length, setup);
+ if (setup->request == USB_REQ_SET_FEATURE &&
+ setup->requesttype == USB_RT_PORT &&
+ setup->value == cpu_to_le16(USB_PORT_FEAT_RESET) &&
+ err == -ENXIO) {
+ /* Device handed over to companion after port reset */
+ uc_priv->companion_device_count++;
+ }
+
+ return err;
}
int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
@@ -65,6 +80,42 @@ int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
return ops->bulk(bus, udev, pipe, buffer, length);
}
+struct int_queue *create_int_queue(struct usb_device *udev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->create_int_queue)
+ return NULL;
+
+ return ops->create_int_queue(bus, udev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *udev, struct int_queue *queue)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->poll_int_queue)
+ return NULL;
+
+ return ops->poll_int_queue(bus, udev, queue);
+}
+
+int destroy_int_queue(struct usb_device *udev, struct int_queue *queue)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->destroy_int_queue)
+ return -ENOSYS;
+
+ return ops->destroy_int_queue(bus, udev, queue);
+}
+
int usb_alloc_device(struct usb_device *udev)
{
struct udevice *bus = udev->controller_dev;
@@ -81,12 +132,16 @@ int usb_stop(void)
{
struct udevice *bus;
struct uclass *uc;
+ struct usb_uclass_priv *uc_priv;
int err = 0, ret;
/* De-activate any devices that have been activated */
ret = uclass_get(UCLASS_USB, &uc);
if (ret)
return ret;
+
+ uc_priv = uc->priv;
+
uclass_foreach_dev(bus, uc) {
ret = device_remove(bus);
if (ret && !err)
@@ -106,12 +161,13 @@ int usb_stop(void)
#endif
usb_stor_reset();
usb_hub_reset();
+ uc_priv->companion_device_count = 0;
usb_started = 0;
return err;
}
-static int usb_scan_bus(struct udevice *bus, bool recurse)
+static void usb_scan_bus(struct udevice *bus, bool recurse)
{
struct usb_bus_priv *priv;
struct udevice *dev;
@@ -121,16 +177,22 @@ static int usb_scan_bus(struct udevice *bus, bool recurse)
assert(recurse); /* TODO: Support non-recusive */
+ printf("scanning bus %d for devices... ", bus->seq);
+ debug("\n");
ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
if (ret)
- return ret;
-
- return priv->next_addr;
+ printf("failed, error %d\n", ret);
+ else if (priv->next_addr == 0)
+ printf("No USB Device found\n");
+ else
+ printf("%d USB Device(s) found\n", priv->next_addr);
}
int usb_init(void)
{
int controllers_initialized = 0;
+ struct usb_uclass_priv *uc_priv;
+ struct usb_bus_priv *priv;
struct udevice *bus;
struct uclass *uc;
int count = 0;
@@ -143,11 +205,12 @@ int usb_init(void)
if (ret)
return ret;
+ uc_priv = uc->priv;
+
uclass_foreach_dev(bus, uc) {
/* init low_level USB */
+ printf("USB%d: ", count);
count++;
- printf("USB");
- printf("%d: ", bus->seq);
ret = device_probe(bus);
if (ret == -ENODEV) { /* No such device. */
puts("Port not available.\n");
@@ -159,23 +222,39 @@ int usb_init(void)
printf("probe failed, error %d\n", ret);
continue;
}
- /*
- * lowlevel init is OK, now scan the bus for devices
- * i.e. search HUBs and configure them
- */
controllers_initialized++;
- printf("scanning bus %d for devices... ", bus->seq);
- debug("\n");
- ret = usb_scan_bus(bus, true);
- if (ret < 0)
- printf("failed, error %d\n", ret);
- else if (!ret)
- printf("No USB Device found\n");
- else
- printf("%d USB Device(s) found\n", ret);
usb_started = true;
}
+ /*
+ * lowlevel init done, now scan the bus for devices i.e. search HUBs
+ * and configure them, first scan primary controllers.
+ */
+ uclass_foreach_dev(bus, uc) {
+ if (!device_active(bus))
+ continue;
+
+ priv = dev_get_uclass_priv(bus);
+ if (!priv->companion)
+ usb_scan_bus(bus, true);
+ }
+
+ /*
+ * Now that the primary controllers have been scanned and have handed
+ * over any devices they do not understand to their companions, scan
+ * the companions if necessary.
+ */
+ if (uc_priv->companion_device_count) {
+ uclass_foreach_dev(bus, uc) {
+ if (!device_active(bus))
+ continue;
+
+ priv = dev_get_uclass_priv(bus);
+ if (priv->companion)
+ usb_scan_bus(bus, true);
+ }
+ }
+
debug("scan end\n");
/* if we were not able to find at least one working bus, bail out */
if (!count)
@@ -477,9 +556,7 @@ int usb_scan_device(struct udevice *parent, int port,
*devp = NULL;
memset(udev, '\0', sizeof(*udev));
- ret = usb_get_bus(parent, &udev->controller_dev);
- if (ret)
- return ret;
+ udev->controller_dev = usb_get_bus(parent);
priv = dev_get_uclass_priv(udev->controller_dev);
/*
@@ -536,11 +613,7 @@ int usb_scan_device(struct udevice *parent, int port,
plat = dev_get_parent_platdata(dev);
debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat);
plat->devnum = udev->devnum;
- plat->speed = udev->speed;
- plat->slot_id = udev->slot_id;
- plat->portnr = port;
- debug("** device '%s': stashing slot_id=%d\n", dev->name,
- plat->slot_id);
+ plat->udev = udev;
priv->next_addr++;
ret = device_probe(dev);
if (ret) {
@@ -579,45 +652,55 @@ int usb_child_post_bind(struct udevice *dev)
return 0;
}
-int usb_get_bus(struct udevice *dev, struct udevice **busp)
+struct udevice *usb_get_bus(struct udevice *dev)
{
struct udevice *bus;
- *busp = NULL;
for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; )
bus = bus->parent;
if (!bus) {
/* By design this cannot happen */
assert(bus);
debug("USB HUB '%s' does not have a controller\n", dev->name);
- return -EXDEV;
}
- *busp = bus;
- return 0;
+ return bus;
}
int usb_child_pre_probe(struct udevice *dev)
{
- struct udevice *bus;
struct usb_device *udev = dev_get_parentdata(dev);
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
int ret;
- ret = usb_get_bus(dev, &bus);
- if (ret)
- return ret;
- udev->controller_dev = bus;
- udev->dev = dev;
- udev->devnum = plat->devnum;
- udev->slot_id = plat->slot_id;
- udev->portnr = plat->portnr;
- udev->speed = plat->speed;
- debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id);
-
- ret = usb_select_config(udev);
- if (ret)
- return ret;
+ if (plat->udev) {
+ /*
+ * Copy over all the values set in the on stack struct
+ * usb_device in usb_scan_device() to our final struct
+ * usb_device for this dev.
+ */
+ *udev = *(plat->udev);
+ /* And clear plat->udev as it will not be valid for long */
+ plat->udev = NULL;
+ udev->dev = dev;
+ } else {
+ /*
+ * This happens with devices which are explicitly bound
+ * instead of being discovered through usb_scan_device()
+ * such as sandbox emul devices.
+ */
+ udev->dev = dev;
+ udev->controller_dev = usb_get_bus(dev);
+ udev->devnum = plat->devnum;
+
+ /*
+ * udev did not go through usb_scan_device(), so we need to
+ * select the config and read the config descriptors.
+ */
+ ret = usb_select_config(udev);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -627,6 +710,7 @@ UCLASS_DRIVER(usb) = {
.name = "usb",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_bind = usb_post_bind,
+ .priv_auto_alloc_size = sizeof(struct usb_uclass_priv),
.per_child_auto_alloc_size = sizeof(struct usb_device),
.per_device_auto_alloc_size = sizeof(struct usb_bus_priv),
.child_post_bind = usb_child_post_bind,
diff --git a/drivers/usb/host/xhci-exynos5.c b/drivers/usb/host/xhci-exynos5.c
index 23c7ecc..a27a796 100644
--- a/drivers/usb/host/xhci-exynos5.c
+++ b/drivers/usb/host/xhci-exynos5.c
@@ -33,36 +33,24 @@
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;
-#ifdef CONFIG_DM_USB
struct exynos_xhci_platdata {
fdt_addr_t hcd_base;
fdt_addr_t phy_base;
struct gpio_desc vbus_gpio;
};
-#endif
/**
* Contains pointers to register base addresses
* for the usb controller.
*/
struct exynos_xhci {
-#ifdef CONFIG_DM_USB
struct usb_platdata usb_plat;
-#endif
struct xhci_ctrl ctrl;
struct exynos_usb3_phy *usb3_phy;
struct xhci_hccr *hcd;
struct dwc3 *dwc3_reg;
-#ifndef CONFIG_DM_USB
- struct gpio_desc vbus_gpio;
-#endif
};
-#ifndef CONFIG_DM_USB
-static struct exynos_xhci exynos;
-#endif
-
-#ifdef CONFIG_DM_USB
static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
{
struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
@@ -102,54 +90,6 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
return 0;
}
-#else
-static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos)
-{
- fdt_addr_t addr;
- unsigned int node;
- int depth;
-
- node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI);
- if (node <= 0) {
- debug("XHCI: Can't get device node for xhci\n");
- return -ENODEV;
- }
-
- /*
- * Get the base address for XHCI controller from the device node
- */
- addr = fdtdec_get_addr(blob, node, "reg");
- if (addr == FDT_ADDR_T_NONE) {
- debug("Can't get the XHCI register base address\n");
- return -ENXIO;
- }
- exynos->hcd = (struct xhci_hccr *)addr;
-
- /* Vbus gpio */
- gpio_request_by_name_nodev(blob, node, "samsung,vbus-gpio", 0,
- &exynos->vbus_gpio, GPIOD_IS_OUT);
-
- depth = 0;
- node = fdtdec_next_compatible_subnode(blob, node,
- COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth);
- if (node <= 0) {
- debug("XHCI: Can't get device node for usb3-phy controller\n");
- return -ENODEV;
- }
-
- /*
- * Get the base address for usbphy from the device node
- */
- exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node,
- "reg");
- if (exynos->usb3_phy == NULL) {
- debug("Can't get the usbphy register address\n");
- return -ENXIO;
- }
-
- return 0;
-}
-#endif
static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
{
@@ -340,53 +280,6 @@ static void exynos_xhci_core_exit(struct exynos_xhci *exynos)
exynos5_usb3_phy_exit(exynos->usb3_phy);
}
-#ifndef CONFIG_DM_USB
-int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
-{
- struct exynos_xhci *ctx = &exynos;
- int ret;
-
-#ifdef CONFIG_OF_CONTROL
- exynos_usb3_parse_dt(gd->fdt_blob, ctx);
-#else
- ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy();
- ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci();
-#endif
-
- ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
-
-#ifdef CONFIG_OF_CONTROL
- /* setup the Vbus gpio here */
- if (dm_gpio_is_valid(&ctx->vbus_gpio))
- dm_gpio_set_value(&ctx->vbus_gpio, 1);
-#endif
-
- ret = exynos_xhci_core_init(ctx);
- if (ret) {
- puts("XHCI: failed to initialize controller\n");
- return -EINVAL;
- }
-
- *hccr = (ctx->hcd);
- *hcor = (struct xhci_hcor *)((uint32_t) *hccr
- + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
-
- debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n",
- (uint32_t)*hccr, (uint32_t)*hcor,
- (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
-
- return 0;
-}
-
-void xhci_hcd_stop(int index)
-{
- struct exynos_xhci *ctx = &exynos;
-
- exynos_xhci_core_exit(ctx);
-}
-#endif
-
-#ifdef CONFIG_DM_USB
static int xhci_usb_probe(struct udevice *dev)
{
struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
@@ -443,4 +336,3 @@ U_BOOT_DRIVER(usb_xhci) = {
.priv_auto_alloc_size = sizeof(struct exynos_xhci),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
-#endif
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 51728b3..2544301 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -88,3 +88,18 @@ config VIDEO_LCD_SPI_MISO
hardware and LCD panel id retrieval (if the panel can report it). The
option takes a string in the format understood by 'name_to_gpio'
function, e.g. PH1 for pin 1 of port H.
+
+config DISPLAY_PORT
+ bool "Enable DisplayPort support"
+ help
+ eDP (Embedded DisplayPort) is a standard widely used in laptops
+ to drive LCD panels. This framework provides support for enabling
+ these displays where supported by the video hardware.
+
+config VIDEO_TEGRA124
+ bool "Enable video support on Tegra124"
+ help
+ Tegra124 supports many video output options including eDP and
+ HDMI. At present only eDP is supported by U-Boot. This option
+ enables this support which can be used on devices which
+ have an eDP display connected.
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f64918e..2ead7f1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -5,6 +5,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
+ifdef CONFIG_DM
+obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
+endif
+
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
@@ -48,3 +52,5 @@ obj-$(CONFIG_FORMIKE) += formike.o
obj-$(CONFIG_LG4573) += lg4573.o
obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-$(CONFIG_VIDEO_PARADE) += parade.o
+
+obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
diff --git a/drivers/video/dp-uclass.c b/drivers/video/dp-uclass.c
new file mode 100644
index 0000000..17f5de9
--- /dev/null
+++ b/drivers/video/dp-uclass.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <displayport.h>
+#include <errno.h>
+
+int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size)
+{
+ struct dm_display_port_ops *ops = display_port_get_ops(dev);
+
+ if (!ops || !ops->read_edid)
+ return -ENOSYS;
+ return ops->read_edid(dev, buf, buf_size);
+}
+
+int display_port_enable(struct udevice *dev, int panel_bpp,
+ const struct display_timing *timing)
+{
+ struct dm_display_port_ops *ops = display_port_get_ops(dev);
+
+ if (!ops || !ops->enable)
+ return -ENOSYS;
+ return ops->enable(dev, panel_bpp, timing);
+}
+
+UCLASS_DRIVER(display_port) = {
+ .id = UCLASS_DISPLAY_PORT,
+ .name = "display_port",
+};
diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra124/Makefile
new file mode 100644
index 0000000..52eedb0
--- /dev/null
+++ b/drivers/video/tegra124/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2014 Google, Inc
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y += display.o
+obj-y += dp.o
+obj-y += sor.o
+obj-y += tegra124-lcd.o
diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra124/display.c
new file mode 100644
index 0000000..7179dbf
--- /dev/null
+++ b/drivers/video/tegra124/display.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Extracted from Chromium coreboot commit 3f59b13d
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <edid.h>
+#include <errno.h>
+#include <displayport.h>
+#include <edid.h>
+#include <fdtdec.h>
+#include <lcd.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/pwm.h>
+#include <asm/arch-tegra/dc.h>
+#include "displayport.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* return in 1000ths of a Hertz */
+static int tegra_dc_calc_refresh(const struct display_timing *timing)
+{
+ int h_total, v_total, refresh;
+ int pclk = timing->pixelclock.typ;
+
+ h_total = timing->hactive.typ + timing->hfront_porch.typ +
+ timing->hback_porch.typ + timing->hsync_len.typ;
+ v_total = timing->vactive.typ + timing->vfront_porch.typ +
+ timing->vback_porch.typ + timing->vsync_len.typ;
+ if (!pclk || !h_total || !v_total)
+ return 0;
+ refresh = pclk / h_total;
+ refresh *= 1000;
+ refresh /= v_total;
+
+ return refresh;
+}
+
+static void print_mode(const struct display_timing *timing)
+{
+ int refresh = tegra_dc_calc_refresh(timing);
+
+ debug("MODE:%dx%d@%d.%03uHz pclk=%d\n",
+ timing->hactive.typ, timing->vactive.typ, refresh / 1000,
+ refresh % 1000, timing->pixelclock.typ);
+}
+
+static int update_display_mode(struct dc_ctlr *disp_ctrl,
+ const struct display_timing *timing,
+ int href_to_sync, int vref_to_sync)
+{
+ print_mode(timing);
+
+ writel(0x1, &disp_ctrl->disp.disp_timing_opt);
+
+ writel(vref_to_sync << 16 | href_to_sync,
+ &disp_ctrl->disp.ref_to_sync);
+
+ writel(timing->vsync_len.typ << 16 | timing->hsync_len.typ,
+ &disp_ctrl->disp.sync_width);
+
+ writel(((timing->vback_porch.typ - vref_to_sync) << 16) |
+ timing->hback_porch.typ, &disp_ctrl->disp.back_porch);
+
+ writel(((timing->vfront_porch.typ + vref_to_sync) << 16) |
+ timing->hfront_porch.typ, &disp_ctrl->disp.front_porch);
+
+ writel(timing->hactive.typ | (timing->vactive.typ << 16),
+ &disp_ctrl->disp.disp_active);
+
+ /**
+ * We want to use PLLD_out0, which is PLLD / 2:
+ * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
+ *
+ * Currently most panels work inside clock range 50MHz~100MHz, and PLLD
+ * has some requirements to have VCO in range 500MHz~1000MHz (see
+ * clock.c for more detail). To simplify calculation, we set
+ * PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values
+ * may be calculated by clock_display, to allow wider frequency range.
+ *
+ * Note ShiftClockDiv is a 7.1 format value.
+ */
+ const u32 shift_clock_div = 1;
+ writel((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
+ ((shift_clock_div - 1) * 2) << SHIFT_CLK_DIVIDER_SHIFT,
+ &disp_ctrl->disp.disp_clk_ctrl);
+ debug("%s: PixelClock=%u, ShiftClockDiv=%u\n", __func__,
+ timing->pixelclock.typ, shift_clock_div);
+ return 0;
+}
+
+static u32 tegra_dc_poll_register(void *reg,
+ u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us)
+{
+ u32 temp = timeout_us;
+ u32 reg_val = 0;
+
+ do {
+ udelay(poll_interval_us);
+ reg_val = readl(reg);
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+ } while ((reg_val & mask) != exp_val);
+
+ if ((reg_val & mask) == exp_val)
+ return 0; /* success */
+
+ return temp;
+}
+
+int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl)
+{
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+
+ if (tegra_dc_poll_register(&disp_ctrl->cmd.state_ctrl,
+ GENERAL_ACT_REQ, 0, 100,
+ DC_POLL_TIMEOUT_MS * 1000)) {
+ debug("dc timeout waiting for DC to stop\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static struct display_timing min_mode = {
+ .hsync_len = { .typ = 1 },
+ .vsync_len = { .typ = 1 },
+ .hback_porch = { .typ = 20 },
+ .vback_porch = { .typ = 0 },
+ .hactive = { .typ = 16 },
+ .vactive = { .typ = 16 },
+ .hfront_porch = { .typ = 1 },
+ .vfront_porch = { .typ = 2 },
+};
+
+/* Disable windows and set minimum raster timings */
+void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx)
+{
+ const int href_to_sync = 0, vref_to_sync = 1;
+ int selected_windows, i;
+
+ selected_windows = readl(&disp_ctrl->cmd.disp_win_header);
+
+ /* Store and clear window options */
+ for (i = 0; i < DC_N_WINDOWS; ++i) {
+ writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header);
+ dc_reg_ctx[i] = readl(&disp_ctrl->win.win_opt);
+ writel(0, &disp_ctrl->win.win_opt);
+ writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl);
+ }
+
+ writel(selected_windows, &disp_ctrl->cmd.disp_win_header);
+
+ /* Store current raster timings and set minimum timings */
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.ref_to_sync);
+ writel(href_to_sync | (vref_to_sync << 16),
+ &disp_ctrl->disp.ref_to_sync);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.sync_width);
+ writel(min_mode.hsync_len.typ | (min_mode.vsync_len.typ << 16),
+ &disp_ctrl->disp.sync_width);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.back_porch);
+ writel(min_mode.hback_porch.typ | (min_mode.vback_porch.typ << 16),
+ &disp_ctrl->disp.back_porch);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.front_porch);
+ writel(min_mode.hfront_porch.typ | (min_mode.vfront_porch.typ << 16),
+ &disp_ctrl->disp.front_porch);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.disp_active);
+ writel(min_mode.hactive.typ | (min_mode.vactive.typ << 16),
+ &disp_ctrl->disp.disp_active);
+
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+}
+
+/* Restore previous windows status and raster timings */
+void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx)
+{
+ int selected_windows, i;
+
+ selected_windows = readl(&disp_ctrl->cmd.disp_win_header);
+
+ for (i = 0; i < DC_N_WINDOWS; ++i) {
+ writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header);
+ writel(dc_reg_ctx[i], &disp_ctrl->win.win_opt);
+ writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl);
+ }
+
+ writel(selected_windows, &disp_ctrl->cmd.disp_win_header);
+
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.ref_to_sync);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.sync_width);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.back_porch);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.front_porch);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.disp_active);
+
+ writel(GENERAL_UPDATE, &disp_ctrl->cmd.state_ctrl);
+}
+
+static int tegra_depth_for_bpp(int bpp)
+{
+ switch (bpp) {
+ case 32:
+ return COLOR_DEPTH_R8G8B8A8;
+ case 16:
+ return COLOR_DEPTH_B5G6R5;
+ default:
+ debug("Unsupported LCD bit depth");
+ return -1;
+ }
+}
+
+static int update_window(struct dc_ctlr *disp_ctrl,
+ u32 frame_buffer, int fb_bits_per_pixel,
+ const struct display_timing *timing)
+{
+ const u32 colour_white = 0xffffff;
+ int colour_depth;
+ u32 val;
+
+ writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
+
+ writel(((timing->vactive.typ << 16) | timing->hactive.typ),
+ &disp_ctrl->win.size);
+ writel(((timing->vactive.typ << 16) |
+ (timing->hactive.typ * fb_bits_per_pixel / 8)),
+ &disp_ctrl->win.prescaled_size);
+ writel(((timing->hactive.typ * fb_bits_per_pixel / 8 + 31) /
+ 32 * 32), &disp_ctrl->win.line_stride);
+
+ colour_depth = tegra_depth_for_bpp(fb_bits_per_pixel);
+ if (colour_depth == -1)
+ return -EINVAL;
+
+ writel(colour_depth, &disp_ctrl->win.color_depth);
+
+ writel(frame_buffer, &disp_ctrl->winbuf.start_addr);
+ writel(0x1000 << V_DDA_INC_SHIFT | 0x1000 << H_DDA_INC_SHIFT,
+ &disp_ctrl->win.dda_increment);
+
+ writel(colour_white, &disp_ctrl->disp.blend_background_color);
+ writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
+ &disp_ctrl->cmd.disp_cmd);
+
+ writel(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+
+ val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
+ val |= GENERAL_UPDATE | WIN_A_UPDATE;
+ writel(val, &disp_ctrl->cmd.state_ctrl);
+
+ /* Enable win_a */
+ val = readl(&disp_ctrl->win.win_opt);
+ writel(val | WIN_ENABLE, &disp_ctrl->win.win_opt);
+
+ return 0;
+}
+
+static int tegra_dc_init(struct dc_ctlr *disp_ctrl)
+{
+ /* do not accept interrupts during initialization */
+ writel(0x00000000, &disp_ctrl->cmd.int_mask);
+ writel(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
+ &disp_ctrl->cmd.state_access);
+ writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
+ writel(0x00000000, &disp_ctrl->win.win_opt);
+ writel(0x00000000, &disp_ctrl->win.byte_swap);
+ writel(0x00000000, &disp_ctrl->win.buffer_ctrl);
+
+ writel(0x00000000, &disp_ctrl->win.pos);
+ writel(0x00000000, &disp_ctrl->win.h_initial_dda);
+ writel(0x00000000, &disp_ctrl->win.v_initial_dda);
+ writel(0x00000000, &disp_ctrl->win.dda_increment);
+ writel(0x00000000, &disp_ctrl->win.dv_ctrl);
+
+ writel(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
+ writel(0x00000000, &disp_ctrl->win.blend_match_select);
+ writel(0x00000000, &disp_ctrl->win.blend_nomatch_select);
+ writel(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
+
+ writel(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
+ writel(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
+ writel(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
+
+ writel(0x00000000, &disp_ctrl->com.crc_checksum);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
+ writel(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
+
+ return 0;
+}
+
+static void dump_config(int panel_bpp, struct display_timing *timing)
+{
+ printf("timing->hactive.typ = %d\n", timing->hactive.typ);
+ printf("timing->vactive.typ = %d\n", timing->vactive.typ);
+ printf("timing->pixelclock.typ = %d\n", timing->pixelclock.typ);
+
+ printf("timing->hfront_porch.typ = %d\n", timing->hfront_porch.typ);
+ printf("timing->hsync_len.typ = %d\n", timing->hsync_len.typ);
+ printf("timing->hback_porch.typ = %d\n", timing->hback_porch.typ);
+
+ printf("timing->vfront_porch.typ %d\n", timing->vfront_porch.typ);
+ printf("timing->vsync_len.typ = %d\n", timing->vsync_len.typ);
+ printf("timing->vback_porch.typ = %d\n", timing->vback_porch.typ);
+
+ printf("panel_bits_per_pixel = %d\n", panel_bpp);
+}
+
+static int display_update_config_from_edid(struct udevice *dp_dev,
+ int *panel_bppp,
+ struct display_timing *timing)
+{
+ u8 buf[EDID_SIZE];
+ int bpc, ret;
+
+ ret = display_port_read_edid(dp_dev, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ ret = edid_get_timing(buf, ret, timing, &bpc);
+ if (ret)
+ return ret;
+
+ /* Use this information if valid */
+ if (bpc != -1)
+ *panel_bppp = bpc * 3;
+
+ return 0;
+}
+
+/* Somewhat torturous method */
+static int get_backlight_info(const void *blob, struct gpio_desc *vdd,
+ struct gpio_desc *enable, int *pwmp)
+{
+ int sor, panel, backlight, power;
+ const u32 *prop;
+ int len;
+ int ret;
+
+ *pwmp = 0;
+ sor = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
+ if (sor < 0)
+ return -ENOENT;
+ panel = fdtdec_lookup_phandle(blob, sor, "nvidia,panel");
+ if (panel < 0)
+ return -ENOENT;
+ backlight = fdtdec_lookup_phandle(blob, panel, "backlight");
+ if (backlight < 0)
+ return -ENOENT;
+ ret = gpio_request_by_name_nodev(blob, backlight, "enable-gpios", 0,
+ enable, GPIOD_IS_OUT);
+ if (ret)
+ return ret;
+ prop = fdt_getprop(blob, backlight, "pwms", &len);
+ if (!prop || len != 3 * sizeof(u32))
+ return -EINVAL;
+ *pwmp = fdt32_to_cpu(prop[1]);
+
+ power = fdtdec_lookup_phandle(blob, backlight, "power-supply");
+ if (power < 0)
+ return -ENOENT;
+ ret = gpio_request_by_name_nodev(blob, power, "gpio", 0, vdd,
+ GPIOD_IS_OUT);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ dm_gpio_free(NULL, enable);
+ return ret;
+}
+
+int display_init(void *lcdbase, int fb_bits_per_pixel,
+ struct display_timing *timing)
+{
+ struct dc_ctlr *dc_ctlr;
+ const void *blob = gd->fdt_blob;
+ struct udevice *dp_dev;
+ const int href_to_sync = 1, vref_to_sync = 1;
+ int panel_bpp = 18; /* default 18 bits per pixel */
+ u32 plld_rate;
+ struct gpio_desc vdd_gpio, enable_gpio;
+ int pwm;
+ int node;
+ int ret;
+
+ ret = uclass_get_device(UCLASS_DISPLAY_PORT, 0, &dp_dev);
+ if (ret)
+ return ret;
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+ if (node < 0)
+ return -ENOENT;
+ dc_ctlr = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+ if (fdtdec_decode_display_timing(blob, node, 0, timing))
+ return -EINVAL;
+
+ ret = display_update_config_from_edid(dp_dev, &panel_bpp, timing);
+ if (ret) {
+ debug("%s: Failed to decode EDID, using defaults\n", __func__);
+ dump_config(panel_bpp, timing);
+ }
+
+ if (!get_backlight_info(blob, &vdd_gpio, &enable_gpio, &pwm)) {
+ dm_gpio_set_value(&vdd_gpio, 1);
+ debug("%s: backlight vdd setting gpio %08x to %d\n",
+ __func__, gpio_get_number(&vdd_gpio), 1);
+ }
+
+ /*
+ * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
+ * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
+ * update_display_mode() for detail.
+ */
+ plld_rate = clock_set_display_rate(timing->pixelclock.typ * 2);
+ if (plld_rate == 0) {
+ printf("dc: clock init failed\n");
+ return -EIO;
+ } else if (plld_rate != timing->pixelclock.typ * 2) {
+ debug("dc: plld rounded to %u\n", plld_rate);
+ timing->pixelclock.typ = plld_rate / 2;
+ }
+
+ /* Init dc */
+ ret = tegra_dc_init(dc_ctlr);
+ if (ret) {
+ debug("dc: init failed\n");
+ return ret;
+ }
+
+ /* Configure dc mode */
+ ret = update_display_mode(dc_ctlr, timing, href_to_sync, vref_to_sync);
+ if (ret) {
+ debug("dc: failed to configure display mode\n");
+ return ret;
+ }
+
+ /* Enable dp */
+ ret = display_port_enable(dp_dev, panel_bpp, timing);
+ if (ret)
+ return ret;
+
+ ret = update_window(dc_ctlr, (ulong)lcdbase, fb_bits_per_pixel, timing);
+ if (ret)
+ return ret;
+
+ /* Set up Tegra PWM to drive the panel backlight */
+ pwm_enable(pwm, 0, 220, 0x2e);
+ udelay(10 * 1000);
+
+ if (dm_gpio_is_valid(&enable_gpio)) {
+ dm_gpio_set_value(&enable_gpio, 1);
+ debug("%s: backlight enable setting gpio %08x to %d\n",
+ __func__, gpio_get_number(&enable_gpio), 1);
+ }
+
+ return 0;
+}
diff --git a/drivers/video/tegra124/displayport.h b/drivers/video/tegra124/displayport.h
new file mode 100644
index 0000000..ace6ab0
--- /dev/null
+++ b/drivers/video/tegra124/displayport.h
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2014, NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _TEGRA_DISPLAYPORT_H
+#define _TEGRA_DISPLAYPORT_H
+
+#include <linux/drm_dp_helper.h>
+
+struct dpaux_ctlr {
+ u32 reserved0;
+ u32 intr_en_aux;
+ u32 reserved2_4;
+ u32 intr_aux;
+};
+
+#define DPAUX_INTR_EN_AUX 0x1
+#define DPAUX_INTR_AUX 0x5
+#define DPAUX_DP_AUXDATA_WRITE_W(i) (0x9 + 4 * (i))
+#define DPAUX_DP_AUXDATA_READ_W(i) (0x19 + 4 * (i))
+#define DPAUX_DP_AUXADDR 0x29
+#define DPAUX_DP_AUXCTL 0x2d
+#define DPAUX_DP_AUXCTL_CMDLEN_SHIFT 0
+#define DPAUX_DP_AUXCTL_CMDLEN_FIELD 0xff
+#define DPAUX_DP_AUXCTL_CMD_SHIFT 12
+#define DPAUX_DP_AUXCTL_CMD_MASK (0xf << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2CWR (0 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2CRD (1 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT (2 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOTWR (4 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOTRD (5 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOTREQWSTAT (6 << 12)
+#define DPAUX_DP_AUXCTL_CMD_AUXWR (8 << 12)
+#define DPAUX_DP_AUXCTL_CMD_AUXRD (9 << 12)
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_SHIFT 16
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_MASK (0x1 << 16)
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_DONE (0 << 16)
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING (1 << 16)
+#define DPAUX_DP_AUXCTL_RST_SHIFT 31
+#define DPAUX_DP_AUXCTL_RST_DEASSERT (0 << 31)
+#define DPAUX_DP_AUXCTL_RST_ASSERT (1 << 31)
+#define DPAUX_DP_AUXSTAT 0x31
+#define DPAUX_DP_AUXSTAT_HPD_STATUS_SHIFT 28
+#define DPAUX_DP_AUXSTAT_HPD_STATUS_UNPLUG (0 << 28)
+#define DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED (1 << 28)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_SHIFT 20
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_MASK (0xf << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_IDLE (0 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_SYNC (1 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_START1 (2 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_COMMAND (3 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_ADDRESS (4 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_LENGTH (5 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_WRITE1 (6 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_READ1 (7 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_GET_M (8 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_STOP1 (9 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_STOP2 (10 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_REPLY (11 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_CLEANUP (12 << 20)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_SHIFT 16
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_MASK (0xf << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_ACK (0 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_NACK (1 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER (2 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_I2CNACK (4 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER (8 << 16)
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_SHIFT 11
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_NOT_PENDING (0 << 11)
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING (1 << 11)
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_SHIFT 10
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_NOT_PENDING (0 << 10)
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING (1 << 10)
+#define DPAUX_DP_AUXSTAT_RX_ERROR_SHIFT 9
+#define DPAUX_DP_AUXSTAT_RX_ERROR_NOT_PENDING (0 << 9)
+#define DPAUX_DP_AUXSTAT_RX_ERROR_PENDING (1 << 9)
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_SHIFT 8
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_NOT_PENDING (0 << 8)
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING (1 << 8)
+#define DPAUX_DP_AUXSTAT_REPLY_M_SHIFT 0
+#define DPAUX_DP_AUXSTAT_REPLY_M_MASK (0xff << 0)
+#define DPAUX_HPD_CONFIG (0x3d)
+#define DPAUX_HPD_IRQ_CONFIG 0x41
+#define DPAUX_DP_AUX_CONFIG 0x45
+#define DPAUX_HYBRID_PADCTL 0x49
+#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_SHIFT 15
+#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_DISABLE (0 << 15)
+#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_ENABLE (1 << 15)
+#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_SHIFT 14
+#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_DISABLE (0 << 14)
+#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_ENABLE (1 << 14)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_SHIFT 12
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_DEFAULT_MASK (0x3 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_60 (0 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_64 (1 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 (2 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_56 (3 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_SHIFT 8
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_DEFAULT_MASK (0x7 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_78 (0 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_60 (1 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_54 (2 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_45 (3 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 (4 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_42 (5 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_39 (6 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_34 (7 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT 2
+#define DPAUX_HYBRID_PADCTL_AUX_DRVI_DEFAULT_MASK (0x3f << 2)
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_SHIFT 1
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_DISABLE (0 << 1)
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE (1 << 1)
+#define DPAUX_HYBRID_PADCTL_MODE_SHIFT 0
+#define DPAUX_HYBRID_PADCTL_MODE_AUX 0
+#define DPAUX_HYBRID_PADCTL_MODE_I2C 1
+#define DPAUX_HYBRID_SPARE 0x4d
+#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP 0
+#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERDOWN 1
+
+#define DP_AUX_DEFER_MAX_TRIES 7
+#define DP_AUX_TIMEOUT_MAX_TRIES 2
+#define DP_POWER_ON_MAX_TRIES 3
+
+#define DP_AUX_MAX_BYTES 16
+
+#define DP_AUX_TIMEOUT_MS 40
+#define DP_DPCP_RETRY_SLEEP_NS 400
+
+static const u32 tegra_dp_vs_regs[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x13, 0x19, 0x1e, 0x28}, /* voltage swing: L0 */
+ {0x1e, 0x25, 0x2d}, /* L1 */
+ {0x28, 0x32}, /* L2 */
+ {0x3c}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x12, 0x17, 0x1b, 0x25},
+ {0x1c, 0x23, 0x2a},
+ {0x25, 0x2f},
+ {0x39},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x12, 0x16, 0x1a, 0x22},
+ {0x1b, 0x20, 0x27},
+ {0x24, 0x2d},
+ {0x36},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x11, 0x14, 0x17, 0x1f},
+ {0x19, 0x1e, 0x24},
+ {0x22, 0x2a},
+ {0x32},
+ },
+};
+
+static const u32 tegra_dp_pe_regs[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x00, 0x09, 0x13, 0x25}, /* voltage swing: L0 */
+ {0x00, 0x0f, 0x1e}, /* L1 */
+ {0x00, 0x14}, /* L2 */
+ {0x00}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x00, 0x0a, 0x14, 0x28},
+ {0x00, 0x0f, 0x1e},
+ {0x00, 0x14},
+ {0x00},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x00, 0x0a, 0x14, 0x28},
+ {0x00, 0x0f, 0x1e},
+ {0x00, 0x14},
+ {0x00},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x00, 0x0a, 0x14, 0x28},
+ {0x00, 0x0f, 0x1e},
+ {0x00, 0x14},
+ {0x00},
+ },
+};
+
+static const u32 tegra_dp_pc_regs[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x00, 0x00, 0x00, 0x00}, /* voltage swing: L0 */
+ {0x00, 0x00, 0x00}, /* L1 */
+ {0x00, 0x00}, /* L2 */
+ {0x00}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x02, 0x02, 0x04, 0x05},
+ {0x02, 0x04, 0x05},
+ {0x04, 0x05},
+ {0x05},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x04, 0x05, 0x08, 0x0b},
+ {0x05, 0x09, 0x0b},
+ {0x08, 0x0a},
+ {0x0b},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x05, 0x09, 0x0b, 0x12},
+ {0x09, 0x0d, 0x12},
+ {0x0b, 0x0f},
+ {0x12},
+ },
+};
+
+static const u32 tegra_dp_tx_pu[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x20, 0x30, 0x40, 0x60}, /* voltage swing: L0 */
+ {0x30, 0x40, 0x60}, /* L1 */
+ {0x40, 0x60}, /* L2 */
+ {0x60}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x20, 0x20, 0x30, 0x50},
+ {0x30, 0x40, 0x50},
+ {0x40, 0x50},
+ {0x60},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x20, 0x20, 0x30, 0x40},
+ {0x30, 0x30, 0x40},
+ {0x40, 0x50},
+ {0x60},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x20, 0x20, 0x20, 0x40},
+ {0x30, 0x30, 0x40},
+ {0x40, 0x40},
+ {0x60},
+ },
+};
+
+enum {
+ DRIVECURRENT_LEVEL0 = 0,
+ DRIVECURRENT_LEVEL1 = 1,
+ DRIVECURRENT_LEVEL2 = 2,
+ DRIVECURRENT_LEVEL3 = 3,
+};
+
+enum {
+ PREEMPHASIS_DISABLED = 0,
+ PREEMPHASIS_LEVEL1 = 1,
+ PREEMPHASIS_LEVEL2 = 2,
+ PREEMPHASIS_LEVEL3 = 3,
+};
+
+enum {
+ POSTCURSOR2_LEVEL0 = 0,
+ POSTCURSOR2_LEVEL1 = 1,
+ POSTCURSOR2_LEVEL2 = 2,
+ POSTCURSOR2_LEVEL3 = 3,
+ POSTCURSOR2_SUPPORTED
+};
+
+static inline int tegra_dp_is_max_vs(u32 pe, u32 vs)
+{
+ return (vs < (DRIVECURRENT_LEVEL3 - pe)) ? 0 : 1;
+}
+
+static inline int tegra_dp_is_max_pe(u32 pe, u32 vs)
+{
+ return (pe < (PREEMPHASIS_LEVEL3 - vs)) ? 0 : 1;
+}
+
+static inline int tegra_dp_is_max_pc(u32 pc)
+{
+ return (pc < POSTCURSOR2_LEVEL3) ? 0 : 1;
+}
+
+/* DPCD definitions which are not defined in drm_dp_helper.h */
+#define DP_DPCD_REV_MAJOR_SHIFT 4
+#define DP_DPCD_REV_MAJOR_MASK (0xf << 4)
+#define DP_DPCD_REV_MINOR_SHIFT 0
+#define DP_DPCD_REV_MINOR_MASK 0xf
+
+#define DP_MAX_LINK_RATE_VAL_1_62_GPBS 0x6
+#define DP_MAX_LINK_RATE_VAL_2_70_GPBS 0xa
+#define DP_MAX_LINK_RATE_VAL_5_40_GPBS 0x4
+
+#define DP_MAX_LANE_COUNT_LANE_1 0x1
+#define DP_MAX_LANE_COUNT_LANE_2 0x2
+#define DP_MAX_LANE_COUNT_LANE_4 0x4
+#define DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES (1 << 6)
+#define DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES (1 << 7)
+
+#define NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT 0
+#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T (0x00000001 << 2)
+#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F (0x00000000 << 2)
+#define NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT 3
+#define NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T (0x00000001 << 5)
+#define NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F (0x00000000 << 5)
+
+#define DP_MAX_DOWNSPREAD_VAL_NONE 0
+#define DP_MAX_DOWNSPREAD_VAL_0_5_PCT 1
+#define DP_MAX_DOWNSPREAD_NO_AUX_HANDSHAKE_LT_T (1 << 6)
+
+#define DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES 1
+#define DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES (1 << 1)
+
+#define DP_LANE_COUNT_SET_ENHANCEDFRAMING_T (1 << 7)
+
+#define DP_TRAINING_PATTERN_SET_SC_DISABLED_T (1 << 5)
+#define NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F (0x00000000 << 5)
+#define NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T (0x00000001 << 5)
+
+#define DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE 0
+#define DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE 1
+
+#define NV_DPCD_TRAINING_LANE0_1_SET2 0x10f
+#define NV_DPCD_TRAINING_LANE2_3_SET2 0x110
+#define NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T (1 << 2)
+#define NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F (0 << 2)
+#define NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T (1 << 6)
+#define NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F (0 << 6)
+#define NV_DPCD_LANEX_SET2_PC2_SHIFT 0
+#define NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT 4
+
+#define NV_DPCD_STATUS_LANEX_CR_DONE_SHIFT 0
+#define NV_DPCD_STATUS_LANEX_CR_DONE_NO (0x00000000)
+#define NV_DPCD_STATUS_LANEX_CR_DONE_YES (0x00000001)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT 1
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_NO (0x00000000 << 1)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES (0x00000001 << 1)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT 2
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_NO (0x00000000 << 2)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES (0x00000001 << 2)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT 4
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_NO (0x00000000 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES (0x00000001 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT 5
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_NO (0x00000000 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES (0x00000001 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT 6
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_NO (0x00000000 << 6)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES (0x00000001 << 6)
+
+#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED (0x00000204)
+#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_NO (0x00000000)
+#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES (0x00000001)
+
+#define NV_DPCD_STATUS_LANEX_CR_DONE_SHIFT 0
+#define NV_DPCD_STATUS_LANEX_CR_DONE_NO (0x00000000)
+#define NV_DPCD_STATUS_LANEX_CR_DONE_YES (0x00000001)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT 1
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_NO (0x00000000 << 1)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES (0x00000001 << 1)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT 2
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_NO (0x00000000 << 2)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES (0x00000001 << 2)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT 4
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_NO (0x00000000 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES (0x00000001 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT 5
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_NO (0x00000000 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES (0x00000001 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT 6
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_NO (0x00000000 << 6)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES (0x00000001 << 6)
+
+#define NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT 0
+#define NV_DPCD_ADJUST_REQ_LANEX_DC_MASK 0x3
+#define NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT 2
+#define NV_DPCD_ADJUST_REQ_LANEX_PE_MASK (0x3 << 2)
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT 4
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK (0x3 << 4)
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT 6
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK (0x3 << 6)
+#define NV_DPCD_ADJUST_REQ_POST_CURSOR2 (0x0000020C)
+#define NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK 0x3
+#define NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(i) (i*2)
+
+#define NV_DPCD_TRAINING_AUX_RD_INTERVAL (0x0000000E)
+#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F (0x00000000 << 2)
+#endif
diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c
new file mode 100644
index 0000000..3c0b721
--- /dev/null
+++ b/drivers/video/tegra124/dp.c
@@ -0,0 +1,1607 @@
+/*
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <displayport.h>
+#include <dm.h>
+#include <div64.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/arch-tegra/dc.h>
+#include "displayport.h"
+#include "edid.h"
+#include "sor.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DO_FAST_LINK_TRAINING 1
+
+struct tegra_dp_plat {
+ ulong base;
+};
+
+struct tegra_dp_priv {
+ struct dpaux_ctlr *regs;
+ struct tegra_dc_sor_data *sor;
+ u8 revision;
+ int enabled;
+};
+
+struct tegra_dp_priv dp_data;
+
+static inline u32 tegra_dpaux_readl(struct tegra_dp_priv *dp, u32 reg)
+{
+ return readl((u32 *)dp->regs + reg);
+}
+
+static inline void tegra_dpaux_writel(struct tegra_dp_priv *dp, u32 reg,
+ u32 val)
+{
+ writel(val, (u32 *)dp->regs + reg);
+}
+
+static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dp_priv *dp,
+ u32 reg, u32 mask, u32 exp_val,
+ u32 poll_interval_us,
+ u32 timeout_us)
+{
+ u32 reg_val = 0;
+ u32 temp = timeout_us;
+
+ do {
+ udelay(poll_interval_us);
+ reg_val = tegra_dpaux_readl(dp, reg);
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+ } while ((reg_val & mask) != exp_val);
+
+ if ((reg_val & mask) == exp_val)
+ return 0; /* success */
+ debug("dpaux_poll_register 0x%x: timeout: (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
+ reg, reg_val, mask, exp_val);
+ return temp;
+}
+
+static inline int tegra_dpaux_wait_transaction(struct tegra_dp_priv *dp)
+{
+ /* According to DP spec, each aux transaction needs to finish
+ within 40ms. */
+ if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL,
+ DPAUX_DP_AUXCTL_TRANSACTREQ_MASK,
+ DPAUX_DP_AUXCTL_TRANSACTREQ_DONE,
+ 100, DP_AUX_TIMEOUT_MS * 1000) != 0) {
+ debug("dp: DPAUX transaction timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int tegra_dc_dpaux_write_chunk(struct tegra_dp_priv *dp, u32 cmd,
+ u32 addr, u8 *data, u32 *size,
+ u32 *aux_stat)
+{
+ int i;
+ u32 reg_val;
+ u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES;
+ u32 defer_retries = DP_AUX_DEFER_MAX_TRIES;
+ u32 temp_data;
+
+ if (*size > DP_AUX_MAX_BYTES)
+ return -1; /* only write one chunk of data */
+
+ /* Make sure the command is write command */
+ switch (cmd) {
+ case DPAUX_DP_AUXCTL_CMD_I2CWR:
+ case DPAUX_DP_AUXCTL_CMD_MOTWR:
+ case DPAUX_DP_AUXCTL_CMD_AUXWR:
+ break;
+ default:
+ debug("dp: aux write cmd 0x%x is invalid\n", cmd);
+ return -EINVAL;
+ }
+
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
+ for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) {
+ memcpy(&temp_data, data, 4);
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data);
+ data += 4;
+ }
+
+ reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL);
+ reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK;
+ reg_val |= cmd;
+ reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD;
+ reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT);
+
+ while ((timeout_retries > 0) && (defer_retries > 0)) {
+ if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) ||
+ (defer_retries != DP_AUX_DEFER_MAX_TRIES))
+ udelay(1);
+
+ reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING;
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val);
+
+ if (tegra_dpaux_wait_transaction(dp))
+ debug("dp: aux write transaction timeout\n");
+
+ *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) {
+ if (timeout_retries-- > 0) {
+ debug("dp: aux write retry (0x%x) -- %d\n",
+ *aux_stat, timeout_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue;
+ } else {
+ debug("dp: aux write got error (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) {
+ if (defer_retries-- > 0) {
+ debug("dp: aux write defer (0x%x) -- %d\n",
+ *aux_stat, defer_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue;
+ } else {
+ debug("dp: aux write defer exceeds max retries (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) ==
+ DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) {
+ *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
+ return 0;
+ } else {
+ debug("dp: aux write failed (0x%x)\n", *aux_stat);
+ return -EIO;
+ }
+ }
+ /* Should never come to here */
+ return -EIO;
+}
+
+static int tegra_dc_dpaux_read_chunk(struct tegra_dp_priv *dp, u32 cmd,
+ u32 addr, u8 *data, u32 *size,
+ u32 *aux_stat)
+{
+ u32 reg_val;
+ u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES;
+ u32 defer_retries = DP_AUX_DEFER_MAX_TRIES;
+
+ if (*size > DP_AUX_MAX_BYTES) {
+ debug("only read one chunk\n");
+ return -EIO; /* only read one chunk */
+ }
+
+ /* Check to make sure the command is read command */
+ switch (cmd) {
+ case DPAUX_DP_AUXCTL_CMD_I2CRD:
+ case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT:
+ case DPAUX_DP_AUXCTL_CMD_MOTRD:
+ case DPAUX_DP_AUXCTL_CMD_AUXRD:
+ break;
+ default:
+ debug("dp: aux read cmd 0x%x is invalid\n", cmd);
+ return -EIO;
+ }
+
+ *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+ if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
+ debug("dp: HPD is not detected\n");
+ return -EIO;
+ }
+
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
+
+ reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL);
+ reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK;
+ reg_val |= cmd;
+ reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD;
+ reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT);
+ while ((timeout_retries > 0) && (defer_retries > 0)) {
+ if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) ||
+ (defer_retries != DP_AUX_DEFER_MAX_TRIES))
+ udelay(DP_DPCP_RETRY_SLEEP_NS * 2);
+
+ reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING;
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val);
+
+ if (tegra_dpaux_wait_transaction(dp))
+ debug("dp: aux read transaction timeout\n");
+
+ *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) {
+ if (timeout_retries-- > 0) {
+ debug("dp: aux read retry (0x%x) -- %d\n",
+ *aux_stat, timeout_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue; /* retry */
+ } else {
+ debug("dp: aux read got error (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) {
+ if (defer_retries-- > 0) {
+ debug("dp: aux read defer (0x%x) -- %d\n",
+ *aux_stat, defer_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue;
+ } else {
+ debug("dp: aux read defer exceeds max retries (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) ==
+ DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) {
+ int i;
+ u32 temp_data[4];
+
+ for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i)
+ temp_data[i] = tegra_dpaux_readl(dp,
+ DPAUX_DP_AUXDATA_READ_W(i));
+
+ *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
+ memcpy(data, temp_data, *size);
+
+ return 0;
+ } else {
+ debug("dp: aux read failed (0x%x\n", *aux_stat);
+ return -EIO;
+ }
+ }
+ /* Should never come to here */
+ debug("%s: can't\n", __func__);
+
+ return -EIO;
+}
+
+static int tegra_dc_dpaux_read(struct tegra_dp_priv *dp, u32 cmd, u32 addr,
+ u8 *data, u32 *size, u32 *aux_stat)
+{
+ u32 finished = 0;
+ u32 cur_size;
+ int ret = 0;
+
+ do {
+ cur_size = *size - finished;
+ if (cur_size > DP_AUX_MAX_BYTES)
+ cur_size = DP_AUX_MAX_BYTES;
+
+ ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr,
+ data, &cur_size, aux_stat);
+ if (ret)
+ break;
+
+ /* cur_size should be the real size returned */
+ addr += cur_size;
+ data += cur_size;
+ finished += cur_size;
+
+ } while (*size > finished);
+ *size = finished;
+
+ return ret;
+}
+
+static int tegra_dc_dp_dpcd_read(struct tegra_dp_priv *dp, u32 cmd,
+ u8 *data_ptr)
+{
+ u32 size = 1;
+ u32 status = 0;
+ int ret;
+
+ ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
+ cmd, data_ptr, &size, &status);
+ if (ret) {
+ debug("dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n",
+ cmd, status);
+ }
+
+ return ret;
+}
+
+static int tegra_dc_dp_dpcd_write(struct tegra_dp_priv *dp, u32 cmd,
+ u8 data)
+{
+ u32 size = 1;
+ u32 status = 0;
+ int ret;
+
+ ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR,
+ cmd, &data, &size, &status);
+ if (ret) {
+ debug("dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n",
+ cmd, status);
+ }
+
+ return ret;
+}
+
+static int tegra_dc_i2c_aux_read(struct tegra_dp_priv *dp, u32 i2c_addr,
+ u8 addr, u8 *data, u32 size, u32 *aux_stat)
+{
+ u32 finished = 0;
+ int ret = 0;
+
+ do {
+ u32 cur_size = min((u32)DP_AUX_MAX_BYTES, size - finished);
+
+ u32 len = 1;
+ ret = tegra_dc_dpaux_write_chunk(
+ dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr,
+ &addr, &len, aux_stat);
+ if (ret) {
+ debug("%s: error sending address to read.\n",
+ __func__);
+ return ret;
+ }
+
+ ret = tegra_dc_dpaux_read_chunk(
+ dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr,
+ data, &cur_size, aux_stat);
+ if (ret) {
+ debug("%s: error reading data.\n", __func__);
+ return ret;
+ }
+
+ /* cur_size should be the real size returned */
+ addr += cur_size;
+ data += cur_size;
+ finished += cur_size;
+ } while (size > finished);
+
+ return finished;
+}
+
+static void tegra_dc_dpaux_enable(struct tegra_dp_priv *dp)
+{
+ /* clear interrupt */
+ tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff);
+ /* do not enable interrupt for now. Enable them when Isr in place */
+ tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0);
+
+ tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL,
+ DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 |
+ DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 |
+ 0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT |
+ DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE);
+
+ tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE,
+ DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP);
+}
+
+#ifdef DEBUG
+static void tegra_dc_dp_dump_link_cfg(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ debug("DP config: cfg_name cfg_value\n");
+ debug(" Lane Count %d\n",
+ link_cfg->max_lane_count);
+ debug(" SupportEnhancedFraming %s\n",
+ link_cfg->support_enhanced_framing ? "Y" : "N");
+ debug(" Bandwidth %d\n",
+ link_cfg->max_link_bw);
+ debug(" bpp %d\n",
+ link_cfg->bits_per_pixel);
+ debug(" EnhancedFraming %s\n",
+ link_cfg->enhanced_framing ? "Y" : "N");
+ debug(" Scramble_enabled %s\n",
+ link_cfg->scramble_ena ? "Y" : "N");
+ debug(" LinkBW %d\n",
+ link_cfg->link_bw);
+ debug(" lane_count %d\n",
+ link_cfg->lane_count);
+ debug(" activespolarity %d\n",
+ link_cfg->activepolarity);
+ debug(" active_count %d\n",
+ link_cfg->active_count);
+ debug(" tu_size %d\n",
+ link_cfg->tu_size);
+ debug(" active_frac %d\n",
+ link_cfg->active_frac);
+ debug(" watermark %d\n",
+ link_cfg->watermark);
+ debug(" hblank_sym %d\n",
+ link_cfg->hblank_sym);
+ debug(" vblank_sym %d\n",
+ link_cfg->vblank_sym);
+}
+#endif
+
+static int _tegra_dp_lower_link_config(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *cfg)
+{
+ switch (cfg->link_bw) {
+ case SOR_LINK_SPEED_G1_62:
+ if (cfg->max_link_bw > SOR_LINK_SPEED_G1_62)
+ cfg->link_bw = SOR_LINK_SPEED_G2_7;
+ cfg->lane_count /= 2;
+ break;
+ case SOR_LINK_SPEED_G2_7:
+ cfg->link_bw = SOR_LINK_SPEED_G1_62;
+ break;
+ case SOR_LINK_SPEED_G5_4:
+ if (cfg->lane_count == 1) {
+ cfg->link_bw = SOR_LINK_SPEED_G2_7;
+ cfg->lane_count = cfg->max_lane_count;
+ } else {
+ cfg->lane_count /= 2;
+ }
+ break;
+ default:
+ debug("dp: Error link rate %d\n", cfg->link_bw);
+ return -ENOLINK;
+ }
+
+ return (cfg->lane_count > 0) ? 0 : -ENOLINK;
+}
+
+/*
+ * Calcuate if given cfg can meet the mode request.
+ * Return 0 if mode is possible, -1 otherwise
+ */
+static int tegra_dc_dp_calc_config(struct tegra_dp_priv *dp,
+ const struct display_timing *timing,
+ struct tegra_dp_link_config *link_cfg)
+{
+ const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000;
+ const u64 f = 100000; /* precision factor */
+ u32 num_linkclk_line; /* Number of link clocks per line */
+ u64 ratio_f; /* Ratio of incoming to outgoing data rate */
+ u64 frac_f;
+ u64 activesym_f; /* Activesym per TU */
+ u64 activecount_f;
+ u32 activecount;
+ u32 activepolarity;
+ u64 approx_value_f;
+ u32 activefrac = 0;
+ u64 accumulated_error_f = 0;
+ u32 lowest_neg_activecount = 0;
+ u32 lowest_neg_activepolarity = 0;
+ u32 lowest_neg_tusize = 64;
+ u32 num_symbols_per_line;
+ u64 lowest_neg_activefrac = 0;
+ u64 lowest_neg_error_f = 64 * f;
+ u64 watermark_f;
+ int i;
+ int neg;
+
+ if (!link_rate || !link_cfg->lane_count || !timing->pixelclock.typ ||
+ !link_cfg->bits_per_pixel)
+ return -1;
+
+ if ((u64)timing->pixelclock.typ * link_cfg->bits_per_pixel >=
+ (u64)link_rate * 8 * link_cfg->lane_count)
+ return -1;
+
+ num_linkclk_line = (u32)(lldiv(link_rate * timing->hactive.typ,
+ timing->pixelclock.typ));
+
+ ratio_f = (u64)timing->pixelclock.typ * link_cfg->bits_per_pixel * f;
+ ratio_f /= 8;
+ do_div(ratio_f, link_rate * link_cfg->lane_count);
+
+ for (i = 64; i >= 32; --i) {
+ activesym_f = ratio_f * i;
+ activecount_f = lldiv(activesym_f, (u32)f) * f;
+ frac_f = activesym_f - activecount_f;
+ activecount = (u32)(lldiv(activecount_f, (u32)f));
+
+ if (frac_f < (lldiv(f, 2))) /* fraction < 0.5 */
+ activepolarity = 0;
+ else {
+ activepolarity = 1;
+ frac_f = f - frac_f;
+ }
+
+ if (frac_f != 0) {
+ /* warning: frac_f should be 64-bit */
+ frac_f = lldiv(f * f, frac_f); /* 1 / fraction */
+ if (frac_f > (15 * f))
+ activefrac = activepolarity ? 1 : 15;
+ else
+ activefrac = activepolarity ?
+ (u32)lldiv(frac_f, (u32)f) + 1 :
+ (u32)lldiv(frac_f, (u32)f);
+ }
+
+ if (activefrac == 1)
+ activepolarity = 0;
+
+ if (activepolarity == 1)
+ approx_value_f = activefrac ? lldiv(
+ (activecount_f + (activefrac * f - f) * f),
+ (activefrac * f)) :
+ activecount_f + f;
+ else
+ approx_value_f = activefrac ?
+ activecount_f + lldiv(f, activefrac) :
+ activecount_f;
+
+ if (activesym_f < approx_value_f) {
+ accumulated_error_f = num_linkclk_line *
+ lldiv(approx_value_f - activesym_f, i);
+ neg = 1;
+ } else {
+ accumulated_error_f = num_linkclk_line *
+ lldiv(activesym_f - approx_value_f, i);
+ neg = 0;
+ }
+
+ if ((neg && (lowest_neg_error_f > accumulated_error_f)) ||
+ (accumulated_error_f == 0)) {
+ lowest_neg_error_f = accumulated_error_f;
+ lowest_neg_tusize = i;
+ lowest_neg_activecount = activecount;
+ lowest_neg_activepolarity = activepolarity;
+ lowest_neg_activefrac = activefrac;
+
+ if (accumulated_error_f == 0)
+ break;
+ }
+ }
+
+ if (lowest_neg_activefrac == 0) {
+ link_cfg->activepolarity = 0;
+ link_cfg->active_count = lowest_neg_activepolarity ?
+ lowest_neg_activecount : lowest_neg_activecount - 1;
+ link_cfg->tu_size = lowest_neg_tusize;
+ link_cfg->active_frac = 1;
+ } else {
+ link_cfg->activepolarity = lowest_neg_activepolarity;
+ link_cfg->active_count = (u32)lowest_neg_activecount;
+ link_cfg->tu_size = lowest_neg_tusize;
+ link_cfg->active_frac = (u32)lowest_neg_activefrac;
+ }
+
+ watermark_f = lldiv(ratio_f * link_cfg->tu_size * (f - ratio_f), f);
+ link_cfg->watermark = (u32)(lldiv(watermark_f + lowest_neg_error_f,
+ f)) + link_cfg->bits_per_pixel / 4 - 1;
+ num_symbols_per_line = (timing->hactive.typ *
+ link_cfg->bits_per_pixel) /
+ (8 * link_cfg->lane_count);
+
+ if (link_cfg->watermark > 30) {
+ debug("dp: sor setting: unable to get a good tusize, force watermark to 30\n");
+ link_cfg->watermark = 30;
+ return -1;
+ } else if (link_cfg->watermark > num_symbols_per_line) {
+ debug("dp: sor setting: force watermark to the number of symbols in the line\n");
+ link_cfg->watermark = num_symbols_per_line;
+ return -1;
+ }
+
+ /*
+ * Refer to dev_disp.ref for more information.
+ * # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width -
+ * SetRasterBlankStart.X - 7) * link_clk / pclk)
+ * - 3 * enhanced_framing - Y
+ * where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12
+ */
+ link_cfg->hblank_sym = (int)lldiv(((uint64_t)timing->hback_porch.typ +
+ timing->hfront_porch.typ + timing->hsync_len.typ - 7) *
+ link_rate, timing->pixelclock.typ) -
+ 3 * link_cfg->enhanced_framing -
+ (12 / link_cfg->lane_count);
+
+ if (link_cfg->hblank_sym < 0)
+ link_cfg->hblank_sym = 0;
+
+
+ /*
+ * Refer to dev_disp.ref for more information.
+ * # symbols/vblank = ((SetRasterBlankStart.X -
+ * SetRasterBlankEen.X - 25) * link_clk / pclk)
+ * - Y - 1;
+ * where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39
+ */
+ link_cfg->vblank_sym = (int)lldiv(((uint64_t)timing->hactive.typ - 25)
+ * link_rate, timing->pixelclock.typ) - (36 /
+ link_cfg->lane_count) - 4;
+
+ if (link_cfg->vblank_sym < 0)
+ link_cfg->vblank_sym = 0;
+
+ link_cfg->is_valid = 1;
+#ifdef DEBUG
+ tegra_dc_dp_dump_link_cfg(dp, link_cfg);
+#endif
+
+ return 0;
+}
+
+static int tegra_dc_dp_init_max_link_cfg(
+ const struct display_timing *timing,
+ struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg)
+{
+ const int drive_current = 0x40404040;
+ const int preemphasis = 0x0f0f0f0f;
+ const int postcursor = 0;
+ u8 dpcd_data;
+ int ret;
+
+ ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LANE_COUNT, &dpcd_data);
+ if (ret)
+ return ret;
+ link_cfg->max_lane_count = dpcd_data & DP_MAX_LANE_COUNT_MASK;
+ link_cfg->tps3_supported = (dpcd_data &
+ DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES) ? 1 : 0;
+
+ link_cfg->support_enhanced_framing =
+ (dpcd_data & DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ?
+ 1 : 0;
+
+ ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_DOWNSPREAD, &dpcd_data);
+ if (ret)
+ return ret;
+ link_cfg->downspread = (dpcd_data & DP_MAX_DOWNSPREAD_VAL_0_5_PCT) ?
+ 1 : 0;
+
+ ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_TRAINING_AUX_RD_INTERVAL,
+ &link_cfg->aux_rd_interval);
+ if (ret)
+ return ret;
+ ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LINK_RATE,
+ &link_cfg->max_link_bw);
+ if (ret)
+ return ret;
+
+ /*
+ * Set to a high value for link training and attach.
+ * Will be re-programmed when dp is enabled.
+ */
+ link_cfg->drive_current = drive_current;
+ link_cfg->preemphasis = preemphasis;
+ link_cfg->postcursor = postcursor;
+
+ ret = tegra_dc_dp_dpcd_read(dp, DP_EDP_CONFIGURATION_CAP, &dpcd_data);
+ if (ret)
+ return ret;
+
+ link_cfg->alt_scramber_reset_cap =
+ (dpcd_data & DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES) ?
+ 1 : 0;
+ link_cfg->only_enhanced_framing =
+ (dpcd_data & DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES) ?
+ 1 : 0;
+
+ link_cfg->lane_count = link_cfg->max_lane_count;
+ link_cfg->link_bw = link_cfg->max_link_bw;
+ link_cfg->enhanced_framing = link_cfg->support_enhanced_framing;
+ link_cfg->frame_in_ms = (1000 / 60) + 1;
+
+ tegra_dc_dp_calc_config(dp, timing, link_cfg);
+ return 0;
+}
+
+static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
+ struct tegra_dc_sor_data *sor, int ena)
+{
+ int ret;
+
+ u8 dpcd_data = ena ?
+ DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE :
+ DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE;
+
+ ret = tegra_dc_dp_dpcd_write(dp, DP_EDP_CONFIGURATION_SET,
+ dpcd_data);
+ if (ret)
+ return ret;
+
+ /* Also reset the scrambler to 0xfffe */
+ tegra_dc_sor_set_internal_panel(sor, ena);
+ return 0;
+}
+
+static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp,
+ struct tegra_dc_sor_data *sor,
+ u8 link_bw)
+{
+ tegra_dc_sor_set_link_bandwidth(sor, link_bw);
+
+ /* Sink side */
+ return tegra_dc_dp_dpcd_write(dp, DP_LINK_BW_SET, link_bw);
+}
+
+static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg,
+ struct tegra_dc_sor_data *sor)
+{
+ u8 dpcd_data;
+ int ret;
+
+ /* check if panel support enhanched_framing */
+ dpcd_data = link_cfg->lane_count;
+ if (link_cfg->enhanced_framing)
+ dpcd_data |= DP_LANE_COUNT_SET_ENHANCEDFRAMING_T;
+ ret = tegra_dc_dp_dpcd_write(dp, DP_LANE_COUNT_SET, dpcd_data);
+ if (ret)
+ return ret;
+
+ tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
+
+ /* Also power down lanes that will not be used */
+ return 0;
+}
+
+static int tegra_dc_dp_link_trained(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 lane;
+ u8 mask;
+ u8 data;
+ int ret;
+
+ for (lane = 0; lane < cfg->lane_count; ++lane) {
+ ret = tegra_dc_dp_dpcd_read(dp, (lane / 2) ?
+ DP_LANE2_3_STATUS : DP_LANE0_1_STATUS,
+ &data);
+ if (ret)
+ return ret;
+ mask = (lane & 1) ?
+ NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES |
+ NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES |
+ NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES :
+ DP_LANE_CR_DONE |
+ DP_LANE_CHANNEL_EQ_DONE |
+ DP_LANE_SYMBOL_LOCKED;
+ if ((data & mask) != mask)
+ return -1;
+ }
+ return 0;
+}
+
+static int tegra_dp_channel_eq_status(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 cnt;
+ u32 n_lanes = cfg->lane_count;
+ u8 data;
+ u8 ce_done = 1;
+ int ret;
+
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ ret = tegra_dc_dp_dpcd_read(dp, DP_LANE0_1_STATUS + cnt, &data);
+ if (ret)
+ return ret;
+
+ if (n_lanes == 1) {
+ ce_done = (data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) &&
+ (data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT));
+ break;
+ } else if (!(data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) ||
+ !(data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)) ||
+ !(data & (0x1 <<
+ NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT)) ||
+ !(data & (0x1 <<
+ NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT)))
+ return -EIO;
+ }
+
+ if (ce_done) {
+ ret = tegra_dc_dp_dpcd_read(dp,
+ DP_LANE_ALIGN_STATUS_UPDATED,
+ &data);
+ if (ret)
+ return ret;
+ if (!(data & NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES))
+ ce_done = 0;
+ }
+
+ return ce_done ? 0 : -EIO;
+}
+
+static int tegra_dp_clock_recovery_status(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 cnt;
+ u32 n_lanes = cfg->lane_count;
+ u8 data_ptr;
+ int ret;
+
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ ret = tegra_dc_dp_dpcd_read(dp, (DP_LANE0_1_STATUS + cnt),
+ &data_ptr);
+ if (ret)
+ return ret;
+
+ if (n_lanes == 1)
+ return (data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ?
+ 1 : 0;
+ else if (!(data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ||
+ !(data_ptr & (NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int tegra_dp_lt_adjust(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
+ u32 pc[4], u8 pc_supported,
+ const struct tegra_dp_link_config *cfg)
+{
+ size_t cnt;
+ u8 data_ptr;
+ u32 n_lanes = cfg->lane_count;
+ int ret;
+
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ ret = tegra_dc_dp_dpcd_read(dp, DP_ADJUST_REQUEST_LANE0_1 + cnt,
+ &data_ptr);
+ if (ret)
+ return ret;
+ pe[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_PE_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT;
+ vs[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_DC_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT;
+ pe[1 + 2 * cnt] =
+ (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT;
+ vs[1 + 2 * cnt] =
+ (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT;
+ }
+ if (pc_supported) {
+ ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_ADJUST_REQ_POST_CURSOR2,
+ &data_ptr);
+ if (ret)
+ return ret;
+ for (cnt = 0; cnt < n_lanes; cnt++) {
+ pc[cnt] = (data_ptr >>
+ NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(cnt)) &
+ NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK;
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_dp_wait_aux_training(struct tegra_dp_priv *dp,
+ bool is_clk_recovery,
+ const struct tegra_dp_link_config *cfg)
+{
+ if (!cfg->aux_rd_interval)
+ udelay(is_clk_recovery ? 200 : 500);
+ else
+ mdelay(cfg->aux_rd_interval * 4);
+}
+
+static void tegra_dp_tpg(struct tegra_dp_priv *dp, u32 tp, u32 n_lanes,
+ const struct tegra_dp_link_config *cfg)
+{
+ u8 data = (tp == training_pattern_disabled)
+ ? (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F)
+ : (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T);
+
+ tegra_dc_sor_set_dp_linkctl(dp->sor, 1, tp, cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, data);
+}
+
+static int tegra_dp_link_config(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u8 dpcd_data;
+ u32 retry;
+ int ret;
+
+ if (link_cfg->lane_count == 0) {
+ debug("dp: error: lane count is 0. Can not set link config.\n");
+ return -ENOLINK;
+ }
+
+ /* Set power state if it is not in normal level */
+ ret = tegra_dc_dp_dpcd_read(dp, DP_SET_POWER, &dpcd_data);
+ if (ret)
+ return ret;
+
+ if (dpcd_data == DP_SET_POWER_D3) {
+ dpcd_data = DP_SET_POWER_D0;
+
+ /* DP spec requires 3 retries */
+ for (retry = 3; retry > 0; --retry) {
+ ret = tegra_dc_dp_dpcd_write(dp, DP_SET_POWER,
+ dpcd_data);
+ if (!ret)
+ break;
+ if (retry == 1) {
+ debug("dp: Failed to set DP panel power\n");
+ return ret;
+ }
+ }
+ }
+
+ /* Enable ASSR if possible */
+ if (link_cfg->alt_scramber_reset_cap) {
+ ret = tegra_dc_dp_set_assr(dp, dp->sor, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = tegra_dp_set_link_bandwidth(dp, dp->sor, link_cfg->link_bw);
+ if (ret) {
+ debug("dp: Failed to set link bandwidth\n");
+ return ret;
+ }
+ ret = tegra_dp_set_lane_count(dp, link_cfg, dp->sor);
+ if (ret) {
+ debug("dp: Failed to set lane count\n");
+ return ret;
+ }
+ tegra_dc_sor_set_dp_linkctl(dp->sor, 1, training_pattern_none,
+ link_cfg);
+
+ return 0;
+}
+
+static int tegra_dp_lower_link_config(struct tegra_dp_priv *dp,
+ const struct display_timing *timing,
+ struct tegra_dp_link_config *cfg)
+{
+ struct tegra_dp_link_config tmp_cfg;
+ int ret;
+
+ tmp_cfg = *cfg;
+ cfg->is_valid = 0;
+
+ ret = _tegra_dp_lower_link_config(dp, cfg);
+ if (!ret)
+ ret = tegra_dc_dp_calc_config(dp, timing, cfg);
+ if (!ret)
+ ret = tegra_dp_link_config(dp, cfg);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ *cfg = tmp_cfg;
+ tegra_dp_link_config(dp, &tmp_cfg);
+ return ret;
+}
+
+static int tegra_dp_lt_config(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
+ u32 pc[4], const struct tegra_dp_link_config *cfg)
+{
+ struct tegra_dc_sor_data *sor = dp->sor;
+ u32 n_lanes = cfg->lane_count;
+ u8 pc_supported = cfg->tps3_supported;
+ u32 cnt;
+ u32 val;
+
+ for (cnt = 0; cnt < n_lanes; cnt++) {
+ u32 mask = 0;
+ u32 pe_reg, vs_reg, pc_reg;
+ u32 shift = 0;
+
+ switch (cnt) {
+ case 0:
+ mask = PR_LANE2_DP_LANE0_MASK;
+ shift = PR_LANE2_DP_LANE0_SHIFT;
+ break;
+ case 1:
+ mask = PR_LANE1_DP_LANE1_MASK;
+ shift = PR_LANE1_DP_LANE1_SHIFT;
+ break;
+ case 2:
+ mask = PR_LANE0_DP_LANE2_MASK;
+ shift = PR_LANE0_DP_LANE2_SHIFT;
+ break;
+ case 3:
+ mask = PR_LANE3_DP_LANE3_MASK;
+ shift = PR_LANE3_DP_LANE3_SHIFT;
+ break;
+ default:
+ debug("dp: incorrect lane cnt\n");
+ return -EINVAL;
+ }
+
+ pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]];
+ vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]];
+ pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]];
+
+ tegra_dp_set_pe_vs_pc(sor, mask, pe_reg << shift,
+ vs_reg << shift, pc_reg << shift,
+ pc_supported);
+ }
+
+ tegra_dp_disable_tx_pu(dp->sor);
+ udelay(20);
+
+ for (cnt = 0; cnt < n_lanes; cnt++) {
+ u32 max_vs_flag = tegra_dp_is_max_vs(pe[cnt], vs[cnt]);
+ u32 max_pe_flag = tegra_dp_is_max_pe(pe[cnt], vs[cnt]);
+
+ val = (vs[cnt] << NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT) |
+ (max_vs_flag ?
+ NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T :
+ NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F) |
+ (pe[cnt] << NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT) |
+ (max_pe_flag ?
+ NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T :
+ NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F);
+ tegra_dc_dp_dpcd_write(dp, (DP_TRAINING_LANE0_SET + cnt), val);
+ }
+
+ if (pc_supported) {
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ u32 max_pc_flag0 = tegra_dp_is_max_pc(pc[cnt]);
+ u32 max_pc_flag1 = tegra_dp_is_max_pc(pc[cnt + 1]);
+ val = (pc[cnt] << NV_DPCD_LANEX_SET2_PC2_SHIFT) |
+ (max_pc_flag0 ?
+ NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T :
+ NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F) |
+ (pc[cnt + 1] <<
+ NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT) |
+ (max_pc_flag1 ?
+ NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T :
+ NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F);
+ tegra_dc_dp_dpcd_write(dp,
+ NV_DPCD_TRAINING_LANE0_1_SET2 +
+ cnt, val);
+ }
+ }
+
+ return 0;
+}
+
+static int _tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4],
+ u32 vs[4], u32 pc[4], u8 pc_supported,
+ u32 n_lanes,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 retry_cnt;
+
+ for (retry_cnt = 0; retry_cnt < 4; retry_cnt++) {
+ int ret;
+
+ if (retry_cnt) {
+ ret = tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported,
+ cfg);
+ if (ret)
+ return ret;
+ tegra_dp_lt_config(dp, pe, vs, pc, cfg);
+ }
+
+ tegra_dp_wait_aux_training(dp, false, cfg);
+
+ if (!tegra_dp_clock_recovery_status(dp, cfg)) {
+ debug("dp: CR failed in channel EQ sequence!\n");
+ break;
+ }
+
+ if (!tegra_dp_channel_eq_status(dp, cfg))
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static int tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
+ u32 pc[4],
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 n_lanes = cfg->lane_count;
+ u8 pc_supported = cfg->tps3_supported;
+ int ret;
+ u32 tp_src = training_pattern_2;
+
+ if (pc_supported)
+ tp_src = training_pattern_3;
+
+ tegra_dp_tpg(dp, tp_src, n_lanes, cfg);
+
+ ret = _tegra_dp_channel_eq(dp, pe, vs, pc, pc_supported, n_lanes, cfg);
+
+ tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg);
+
+ return ret;
+}
+
+static int _tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4],
+ u32 vs[4], u32 pc[4], u8 pc_supported,
+ u32 n_lanes,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 vs_temp[4];
+ u32 retry_cnt = 0;
+
+ do {
+ tegra_dp_lt_config(dp, pe, vs, pc, cfg);
+ tegra_dp_wait_aux_training(dp, true, cfg);
+
+ if (tegra_dp_clock_recovery_status(dp, cfg))
+ return 0;
+
+ memcpy(vs_temp, vs, sizeof(vs_temp));
+ tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported, cfg);
+
+ if (memcmp(vs_temp, vs, sizeof(vs_temp)))
+ retry_cnt = 0;
+ else
+ ++retry_cnt;
+ } while (retry_cnt < 5);
+
+ return -EIO;
+}
+
+static int tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4],
+ u32 vs[4], u32 pc[4],
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 n_lanes = cfg->lane_count;
+ u8 pc_supported = cfg->tps3_supported;
+ int err;
+
+ tegra_dp_tpg(dp, training_pattern_1, n_lanes, cfg);
+
+ err = _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes,
+ cfg);
+ if (err < 0)
+ tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg);
+
+ return err;
+}
+
+static int tegra_dc_dp_full_link_training(struct tegra_dp_priv *dp,
+ const struct display_timing *timing,
+ struct tegra_dp_link_config *cfg)
+{
+ struct tegra_dc_sor_data *sor = dp->sor;
+ int err;
+ u32 pe[4], vs[4], pc[4];
+
+ tegra_sor_precharge_lanes(sor, cfg);
+
+retry_cr:
+ memset(pe, PREEMPHASIS_DISABLED, sizeof(pe));
+ memset(vs, DRIVECURRENT_LEVEL0, sizeof(vs));
+ memset(pc, POSTCURSOR2_LEVEL0, sizeof(pc));
+
+ err = tegra_dp_clk_recovery(dp, pe, vs, pc, cfg);
+ if (err) {
+ if (!tegra_dp_lower_link_config(dp, timing, cfg))
+ goto retry_cr;
+
+ debug("dp: clk recovery failed\n");
+ goto fail;
+ }
+
+ err = tegra_dp_channel_eq(dp, pe, vs, pc, cfg);
+ if (err) {
+ if (!tegra_dp_lower_link_config(dp, timing, cfg))
+ goto retry_cr;
+
+ debug("dp: channel equalization failed\n");
+ goto fail;
+ }
+#ifdef DEBUG
+ tegra_dc_dp_dump_link_cfg(dp, cfg);
+#endif
+ return 0;
+
+fail:
+ return err;
+}
+
+/*
+ * All link training functions are ported from kernel dc driver.
+ * See more details at drivers/video/tegra/dc/dp.c
+ */
+static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg,
+ struct tegra_dc_sor_data *sor)
+{
+ u8 link_bw;
+ u8 lane_count;
+ u16 data16;
+ u32 data32;
+ u32 size;
+ u32 status;
+ int j;
+ u32 mask = 0xffff >> ((4 - link_cfg->lane_count) * 4);
+
+ tegra_dc_sor_set_lane_parm(sor, link_cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_MAIN_LINK_CHANNEL_CODING_SET,
+ DP_SET_ANSI_8B10B);
+
+ /* Send TP1 */
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_1);
+
+ for (j = 0; j < link_cfg->lane_count; ++j)
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24);
+ udelay(520);
+
+ size = sizeof(data16);
+ tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
+ DP_LANE0_1_STATUS, (u8 *)&data16, &size, &status);
+ status = mask & 0x1111;
+ if ((data16 & status) != status) {
+ debug("dp: Link training error for TP1 (%#x, status %#x)\n",
+ data16, status);
+ return -EFAULT;
+ }
+
+ /* enable ASSR */
+ tegra_dc_dp_set_assr(dp, sor, link_cfg->scramble_ena);
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg);
+
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET,
+ link_cfg->link_bw == 20 ? 0x23 : 0x22);
+ for (j = 0; j < link_cfg->lane_count; ++j)
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24);
+ udelay(520);
+
+ size = sizeof(data32);
+ tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, DP_LANE0_1_STATUS,
+ (u8 *)&data32, &size, &status);
+ if ((data32 & mask) != (0x7777 & mask)) {
+ debug("dp: Link training error for TP2/3 (0x%x)\n", data32);
+ return -EFAULT;
+ }
+
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled,
+ link_cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 0);
+
+ if (tegra_dc_dp_link_trained(dp, link_cfg)) {
+ tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count);
+ debug("Fast link training failed, link bw %d, lane # %d\n",
+ link_bw, lane_count);
+ return -EFAULT;
+ }
+
+ debug("Fast link training succeeded, link bw %d, lane %d\n",
+ link_cfg->link_bw, link_cfg->lane_count);
+
+ return 0;
+}
+
+static int tegra_dp_do_link_training(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing,
+ struct tegra_dc_sor_data *sor)
+{
+ u8 link_bw;
+ u8 lane_count;
+ int ret;
+
+ if (DO_FAST_LINK_TRAINING) {
+ ret = tegra_dc_dp_fast_link_training(dp, link_cfg, sor);
+ if (ret) {
+ debug("dp: fast link training failed\n");
+ } else {
+ /*
+ * set to a known-good drive setting if fast link
+ * succeeded. Ignore any error.
+ */
+ ret = tegra_dc_sor_set_voltage_swing(dp->sor, link_cfg);
+ if (ret)
+ debug("Failed to set voltage swing\n");
+ }
+ } else {
+ ret = -ENOSYS;
+ }
+ if (ret) {
+ /* Try full link training then */
+ ret = tegra_dc_dp_full_link_training(dp, timing, link_cfg);
+ if (ret) {
+ debug("dp: full link training failed\n");
+ return ret;
+ }
+ }
+
+ /* Everything is good; double check the link config */
+ tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count);
+
+ if ((link_cfg->link_bw == link_bw) &&
+ (link_cfg->lane_count == lane_count))
+ return 0;
+ else
+ return -EFAULT;
+}
+
+static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg,
+ struct tegra_dc_sor_data *sor,
+ const struct display_timing *timing)
+{
+ struct tegra_dp_link_config temp_cfg;
+
+ if (!timing->pixelclock.typ || !timing->hactive.typ ||
+ !timing->vactive.typ) {
+ debug("dp: error mode configuration");
+ return -EINVAL;
+ }
+ if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) {
+ debug("dp: error link configuration");
+ return -EINVAL;
+ }
+
+ link_cfg->is_valid = 0;
+
+ memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg));
+
+ temp_cfg.link_bw = temp_cfg.max_link_bw;
+ temp_cfg.lane_count = temp_cfg.max_lane_count;
+
+ /*
+ * set to max link config
+ */
+ if ((!tegra_dc_dp_calc_config(dp, timing, &temp_cfg)) &&
+ (!tegra_dp_link_config(dp, &temp_cfg)) &&
+ (!tegra_dp_do_link_training(dp, &temp_cfg, timing, sor)))
+ /* the max link cfg is doable */
+ memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg));
+
+ return link_cfg->is_valid ? 0 : -EFAULT;
+}
+
+static int tegra_dp_hpd_plug(struct tegra_dp_priv *dp)
+{
+ const int vdd_to_hpd_delay_ms = 200;
+ u32 val;
+ ulong start;
+
+ start = get_timer(0);
+ do {
+ val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+ if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)
+ return 0;
+ udelay(100);
+ } while (get_timer(start) < vdd_to_hpd_delay_ms);
+
+ return -EIO;
+}
+
+static int tegra_dc_dp_sink_out_of_sync(struct tegra_dp_priv *dp, u32 delay_ms)
+{
+ u8 dpcd_data;
+ int out_of_sync;
+ int ret;
+
+ debug("%s: delay=%d\n", __func__, delay_ms);
+ mdelay(delay_ms);
+ ret = tegra_dc_dp_dpcd_read(dp, DP_SINK_STATUS, &dpcd_data);
+ if (ret)
+ return ret;
+
+ out_of_sync = !(dpcd_data & DP_SINK_STATUS_PORT0_IN_SYNC);
+ if (out_of_sync)
+ debug("SINK receive port 0 out of sync, data=%x\n", dpcd_data);
+ else
+ debug("SINK is in synchronization\n");
+
+ return out_of_sync;
+}
+
+static int tegra_dc_dp_check_sink(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing)
+{
+ const int max_retry = 5;
+ int delay_frame;
+ int retries;
+
+ /*
+ * DP TCON may skip some main stream frames, thus we need to wait
+ * some delay before reading the DPCD SINK STATUS register, starting
+ * from 5
+ */
+ delay_frame = 5;
+
+ retries = max_retry;
+ do {
+ int ret;
+
+ if (!tegra_dc_dp_sink_out_of_sync(dp, link_cfg->frame_in_ms *
+ delay_frame))
+ return 0;
+
+ debug("%s: retries left %d\n", __func__, retries);
+ if (!retries--) {
+ printf("DP: Out of sync after %d retries\n", max_retry);
+ return -EIO;
+ }
+ ret = tegra_dc_sor_detach(dp->sor);
+ if (ret)
+ return ret;
+ if (tegra_dc_dp_explore_link_cfg(dp, link_cfg, dp->sor,
+ timing)) {
+ debug("dp: %s: error to configure link\n", __func__);
+ continue;
+ }
+
+ tegra_dc_sor_set_power_state(dp->sor, 1);
+ tegra_dc_sor_attach(dp->sor, link_cfg, timing);
+
+ /* Increase delay_frame for next try in case the sink is
+ skipping more frames */
+ delay_frame += 10;
+ } while (1);
+}
+
+int tegra_dp_enable(struct udevice *dev, int panel_bpp,
+ const struct display_timing *timing)
+{
+ struct tegra_dp_priv *priv = dev_get_priv(dev);
+ struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg;
+ struct tegra_dc_sor_data *sor;
+ int data;
+ int retry;
+ int ret;
+
+ memset(link_cfg, '\0', sizeof(*link_cfg));
+ link_cfg->is_valid = 0;
+ link_cfg->scramble_ena = 1;
+
+ tegra_dc_dpaux_enable(priv);
+
+ if (tegra_dp_hpd_plug(priv) < 0) {
+ debug("dp: hpd plug failed\n");
+ return -EIO;
+ }
+
+ link_cfg->bits_per_pixel = panel_bpp;
+ if (tegra_dc_dp_init_max_link_cfg(timing, priv, link_cfg)) {
+ debug("dp: failed to init link configuration\n");
+ return -ENOLINK;
+ }
+
+ ret = tegra_dc_sor_init(&sor);
+ if (ret)
+ return ret;
+ priv->sor = sor;
+ ret = tegra_dc_sor_enable_dp(sor, link_cfg);
+ if (ret)
+ return ret;
+
+ tegra_dc_sor_set_panel_power(sor, 1);
+
+ /* Write power on to DPCD */
+ data = DP_SET_POWER_D0;
+ retry = 0;
+ do {
+ ret = tegra_dc_dp_dpcd_write(priv, DP_SET_POWER, data);
+ } while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret);
+
+ if (ret || retry >= DP_POWER_ON_MAX_TRIES) {
+ debug("dp: failed to power on panel (0x%x)\n", ret);
+ return -ENETUNREACH;
+ goto error_enable;
+ }
+
+ /* Confirm DP plugging status */
+ if (!(tegra_dpaux_readl(priv, DPAUX_DP_AUXSTAT) &
+ DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
+ debug("dp: could not detect HPD\n");
+ return -ENXIO;
+ }
+
+ /* Check DP version */
+ if (tegra_dc_dp_dpcd_read(priv, DP_DPCD_REV, &priv->revision)) {
+ debug("dp: failed to read the revision number from sink\n");
+ return -EIO;
+ }
+
+ if (tegra_dc_dp_explore_link_cfg(priv, link_cfg, sor, timing)) {
+ debug("dp: error configuring link\n");
+ return -ENOMEDIUM;
+ }
+
+ tegra_dc_sor_set_power_state(sor, 1);
+ ret = tegra_dc_sor_attach(sor, link_cfg, timing);
+ if (ret && ret != -EEXIST)
+ return ret;
+
+ /*
+ * This takes a long time, but can apparently resolve a failure to
+ * bring up the display correctly.
+ */
+ if (0) {
+ ret = tegra_dc_dp_check_sink(priv, link_cfg, timing);
+ if (ret)
+ return ret;
+ }
+
+ /* Power down the unused lanes to save power - a few hundred mW */
+ tegra_dc_sor_power_down_unused_lanes(sor, link_cfg);
+
+ priv->enabled = true;
+error_enable:
+ return 0;
+}
+
+static int tegra_dp_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tegra_dp_plat *plat = dev_get_platdata(dev);
+ const void *blob = gd->fdt_blob;
+
+ plat->base = fdtdec_get_addr(blob, dev->of_offset, "reg");
+
+ return 0;
+}
+
+static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size)
+{
+ struct tegra_dp_priv *priv = dev_get_priv(dev);
+ const int tegra_edid_i2c_address = 0x50;
+ u32 aux_stat = 0;
+
+ tegra_dc_dpaux_enable(priv);
+
+ return tegra_dc_i2c_aux_read(priv, tegra_edid_i2c_address, 0, buf,
+ buf_size, &aux_stat);
+}
+
+static const struct dm_display_port_ops dp_tegra_ops = {
+ .read_edid = tegra_dp_read_edid,
+ .enable = tegra_dp_enable,
+};
+
+static int dp_tegra_probe(struct udevice *dev)
+{
+ struct tegra_dp_plat *plat = dev_get_platdata(dev);
+ struct tegra_dp_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct dpaux_ctlr *)plat->base;
+ priv->enabled = false;
+
+ return 0;
+}
+
+static const struct udevice_id tegra_dp_ids[] = {
+ { .compatible = "nvidia,tegra124-dpaux" },
+ { }
+};
+
+U_BOOT_DRIVER(dp_tegra) = {
+ .name = "dpaux_tegra",
+ .id = UCLASS_DISPLAY_PORT,
+ .of_match = tegra_dp_ids,
+ .ofdata_to_platdata = tegra_dp_ofdata_to_platdata,
+ .probe = dp_tegra_probe,
+ .ops = &dp_tegra_ops,
+ .priv_auto_alloc_size = sizeof(struct tegra_dp_priv),
+ .platdata_auto_alloc_size = sizeof(struct tegra_dp_plat),
+};
diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c
new file mode 100644
index 0000000..aa3d80c
--- /dev/null
+++ b/drivers/video/tegra124/sor.c
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch-tegra/dc.h>
+#include "displayport.h"
+#include "sor.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DEBUG_SOR 0
+
+#define APBDEV_PMC_DPD_SAMPLE 0x20
+#define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE 0
+#define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE 1
+#define APBDEV_PMC_SEL_DPD_TIM 0x1c8
+#define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT 0x7f
+#define APBDEV_PMC_IO_DPD2_REQ 0x1c0
+#define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT 25
+#define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF (0 << 25)
+#define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON (1 << 25)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT 30
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK (0x3 << 30)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE (0 << 30)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF (1 << 30)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON (2 << 30)
+#define APBDEV_PMC_IO_DPD2_STATUS 0x1c4
+#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT 25
+#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25)
+#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25)
+
+static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
+{
+ return readl((u32 *)sor->base + reg);
+}
+
+static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, u32 reg,
+ u32 val)
+{
+ writel(val, (u32 *)sor->base + reg);
+}
+
+static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
+ u32 reg, u32 mask, u32 val)
+{
+ u32 reg_val = tegra_sor_readl(sor, reg);
+ reg_val &= ~mask;
+ reg_val |= val;
+ tegra_sor_writel(sor, reg, reg_val);
+}
+
+void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor)
+{
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
+}
+
+void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, u32 pe_reg,
+ u32 vs_reg, u32 pc_reg, u8 pc_supported)
+{
+ tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
+ tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
+ if (pc_supported) {
+ tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask,
+ pc_reg);
+ }
+}
+
+static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
+ u32 mask, u32 exp_val,
+ int poll_interval_us, int timeout_ms)
+{
+ u32 reg_val = 0;
+ ulong start;
+
+ start = get_timer(0);
+ do {
+ reg_val = tegra_sor_readl(sor, reg);
+ if (((reg_val & mask) == exp_val))
+ return 0;
+ udelay(poll_interval_us);
+ } while (get_timer(start) < timeout_ms);
+
+ debug("sor_poll_register 0x%x: timeout, (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
+ reg, reg_val, mask, exp_val);
+
+ return -ETIMEDOUT;
+}
+
+int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
+{
+ u32 reg_val;
+ u32 orig_val;
+
+ orig_val = tegra_sor_readl(sor, PWR);
+
+ reg_val = pu_pd ? PWR_NORMAL_STATE_PU :
+ PWR_NORMAL_STATE_PD; /* normal state only */
+
+ if (reg_val == orig_val)
+ return 0; /* No update needed */
+
+ reg_val |= PWR_SETTING_NEW_TRIGGER;
+ tegra_sor_writel(sor, PWR, reg_val);
+
+ /* Poll to confirm it is done */
+ if (tegra_dc_sor_poll_register(sor, PWR,
+ PWR_SETTING_NEW_DEFAULT_MASK,
+ PWR_SETTING_NEW_DONE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ debug("dc timeout waiting for SOR_PWR = NEW_DONE\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+ u8 training_pattern,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
+
+ if (ena)
+ reg_val |= DP_LINKCTL_ENABLE_YES;
+ else
+ reg_val &= DP_LINKCTL_ENABLE_NO;
+
+ reg_val &= ~DP_LINKCTL_TUSIZE_MASK;
+ reg_val |= (link_cfg->tu_size << DP_LINKCTL_TUSIZE_SHIFT);
+
+ if (link_cfg->enhanced_framing)
+ reg_val |= DP_LINKCTL_ENHANCEDFRAME_ENABLE;
+
+ tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
+
+ switch (training_pattern) {
+ case training_pattern_1:
+ tegra_sor_writel(sor, DP_TPG, 0x41414141);
+ break;
+ case training_pattern_2:
+ case training_pattern_3:
+ reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ?
+ 0x43434343 : 0x42424242;
+ tegra_sor_writel(sor, DP_TPG, reg_val);
+ break;
+ default:
+ tegra_sor_writel(sor, DP_TPG, 0x50505050);
+ break;
+ }
+}
+
+static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
+ int pu, int is_lvds)
+{
+ u32 reg_val;
+
+ /* SOR lane sequencer */
+ if (pu) {
+ reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
+ LANE_SEQ_CTL_SEQUENCE_DOWN |
+ LANE_SEQ_CTL_NEW_POWER_STATE_PU;
+ } else {
+ reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
+ LANE_SEQ_CTL_SEQUENCE_UP |
+ LANE_SEQ_CTL_NEW_POWER_STATE_PD;
+ }
+
+ if (is_lvds)
+ reg_val |= 15 << LANE_SEQ_CTL_DELAY_SHIFT;
+ else
+ reg_val |= 1 << LANE_SEQ_CTL_DELAY_SHIFT;
+
+ tegra_sor_writel(sor, LANE_SEQ_CTL, reg_val);
+
+ if (tegra_dc_sor_poll_register(sor, LANE_SEQ_CTL,
+ LANE_SEQ_CTL_SETTING_MASK,
+ LANE_SEQ_CTL_SETTING_NEW_DONE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ debug("dp: timeout while waiting for SOR lane sequencer to power down lanes\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
+ u32 lane_count, int pu)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
+
+ if (pu) {
+ switch (lane_count) {
+ case 4:
+ reg_val |= (DP_PADCTL_PD_TXD_3_NO |
+ DP_PADCTL_PD_TXD_2_NO);
+ /* fall through */
+ case 2:
+ reg_val |= DP_PADCTL_PD_TXD_1_NO;
+ case 1:
+ reg_val |= DP_PADCTL_PD_TXD_0_NO;
+ break;
+ default:
+ debug("dp: invalid lane number %d\n", lane_count);
+ return -1;
+ }
+
+ tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
+ tegra_dc_sor_set_lane_count(sor, lane_count);
+ }
+
+ return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
+}
+
+void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
+ int power_up)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
+
+ if (power_up)
+ reg_val |= DP_PADCTL_PAD_CAL_PD_POWERUP;
+ else
+ reg_val &= ~DP_PADCTL_PAD_CAL_PD_POWERUP;
+
+ tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
+}
+
+static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div,
+ u32 pwm_dutycycle)
+{
+ tegra_sor_writel(sor, PWM_DIV, pwm_div);
+ tegra_sor_writel(sor, PWM_CTL,
+ (pwm_dutycycle & PWM_CTL_DUTY_CYCLE_MASK) |
+ PWM_CTL_SETTING_NEW_TRIGGER);
+
+ if (tegra_dc_sor_poll_register(sor, PWM_CTL,
+ PWM_CTL_SETTING_NEW_SHIFT,
+ PWM_CTL_SETTING_NEW_DONE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ debug("dp: timeout while waiting for SOR PWM setting\n");
+ }
+}
+
+static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 reg_val;
+
+ tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
+ reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum));
+ reg_val &= ~DP_CONFIG_WATERMARK_MASK;
+ reg_val |= link_cfg->watermark;
+ reg_val &= ~DP_CONFIG_ACTIVESYM_COUNT_MASK;
+ reg_val |= (link_cfg->active_count <<
+ DP_CONFIG_ACTIVESYM_COUNT_SHIFT);
+ reg_val &= ~DP_CONFIG_ACTIVESYM_FRAC_MASK;
+ reg_val |= (link_cfg->active_frac <<
+ DP_CONFIG_ACTIVESYM_FRAC_SHIFT);
+ if (link_cfg->activepolarity)
+ reg_val |= DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
+ else
+ reg_val &= ~DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
+ reg_val |= (DP_CONFIG_ACTIVESYM_CNTL_ENABLE |
+ DP_CONFIG_RD_RESET_VAL_NEGATIVE);
+
+ tegra_sor_writel(sor, DP_CONFIG(sor->portnum), reg_val);
+
+ /* program h/vblank sym */
+ tegra_sor_write_field(sor, DP_AUDIO_HBLANK_SYMBOLS,
+ DP_AUDIO_HBLANK_SYMBOLS_MASK,
+ link_cfg->hblank_sym);
+
+ tegra_sor_write_field(sor, DP_AUDIO_VBLANK_SYMBOLS,
+ DP_AUDIO_VBLANK_SYMBOLS_MASK,
+ link_cfg->vblank_sym);
+}
+
+static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor)
+{
+ tegra_sor_writel(sor, SUPER_STATE0, 0);
+ tegra_sor_writel(sor, SUPER_STATE0, 1);
+ tegra_sor_writel(sor, SUPER_STATE0, 0);
+}
+
+static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor)
+{
+ tegra_sor_writel(sor, STATE0, 0);
+ tegra_sor_writel(sor, STATE0, 1);
+ tegra_sor_writel(sor, STATE0, 0);
+}
+
+static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
+{
+ u32 reg_val;
+ void *pmc_base = sor->pmc_base;
+
+ if (up) {
+ writel(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE,
+ pmc_base + APBDEV_PMC_DPD_SAMPLE);
+ writel(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM);
+ }
+
+ reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_REQ);
+ reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON ||
+ APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK);
+
+ reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON |
+ APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF :
+ APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF |
+ APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON;
+
+ writel(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ);
+
+ /* Polling */
+ u32 temp = 10 * 1000;
+ do {
+ udelay(20);
+ reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_STATUS);
+ if (temp > 20)
+ temp -= 20;
+ else
+ break;
+ } while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0);
+
+ if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) {
+ debug("PMC_IO_DPD2 polling failed (0x%x)\n", reg_val);
+ return -EIO;
+ }
+
+ if (up) {
+ writel(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE,
+ pmc_base + APBDEV_PMC_DPD_SAMPLE);
+ }
+
+ return 0;
+}
+
+void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
+ if (is_int)
+ reg_val |= DP_SPARE_PANEL_INTERNAL;
+ else
+ reg_val &= ~DP_SPARE_PANEL_INTERNAL;
+
+ reg_val |= DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK |
+ DP_SPARE_SEQ_ENABLE_YES;
+ tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val);
+}
+
+void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+ u8 *lane_count)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, CLK_CNTRL);
+ *link_bw = (reg_val & CLK_CNTRL_DP_LINK_SPEED_MASK)
+ >> CLK_CNTRL_DP_LINK_SPEED_SHIFT;
+ reg_val = tegra_sor_readl(sor,
+ DP_LINKCTL(sor->portnum));
+
+ switch (reg_val & DP_LINKCTL_LANECOUNT_MASK) {
+ case DP_LINKCTL_LANECOUNT_ZERO:
+ *lane_count = 0;
+ break;
+ case DP_LINKCTL_LANECOUNT_ONE:
+ *lane_count = 1;
+ break;
+ case DP_LINKCTL_LANECOUNT_TWO:
+ *lane_count = 2;
+ break;
+ case DP_LINKCTL_LANECOUNT_FOUR:
+ *lane_count = 4;
+ break;
+ default:
+ printf("Unknown lane count\n");
+ }
+}
+
+void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw)
+{
+ tegra_sor_write_field(sor, CLK_CNTRL,
+ CLK_CNTRL_DP_LINK_SPEED_MASK,
+ link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT);
+}
+
+void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
+ reg_val &= ~DP_LINKCTL_LANECOUNT_MASK;
+ switch (lane_count) {
+ case 0:
+ break;
+ case 1:
+ reg_val |= DP_LINKCTL_LANECOUNT_ONE;
+ break;
+ case 2:
+ reg_val |= DP_LINKCTL_LANECOUNT_TWO;
+ break;
+ case 4:
+ reg_val |= DP_LINKCTL_LANECOUNT_FOUR;
+ break;
+ default:
+ /* 0 should be handled earlier. */
+ printf("dp: Invalid lane count %d\n", lane_count);
+ return;
+ }
+ tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
+}
+
+/*
+ * The SOR power sequencer does not work for t124 so SW has to
+ * go through the power sequence manually
+ * Power up steps from spec:
+ * STEP PDPORT PDPLL PDBG PLLVCOD PLLCAPD E_DPD PDCAL
+ * 1 1 1 1 1 1 1 1
+ * 2 1 1 1 1 1 0 1
+ * 3 1 1 0 1 1 0 1
+ * 4 1 0 0 0 0 0 1
+ * 5 0 0 0 0 0 0 1
+ */
+static int tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor, int is_lvds)
+{
+ int ret;
+
+ if (sor->power_is_up)
+ return 0;
+
+ /* Set link bw */
+ tegra_dc_sor_set_link_bandwidth(sor, is_lvds ?
+ CLK_CNTRL_DP_LINK_SPEED_LVDS :
+ CLK_CNTRL_DP_LINK_SPEED_G1_62);
+
+ /* step 1 */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */
+ PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */
+ PLL2_AUX7_PORT_POWERDOWN_ENABLE |
+ PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE |
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE);
+ tegra_sor_write_field(sor, PLL0, PLL0_PWR_MASK | /* PDPLL */
+ PLL0_VCOPD_MASK, /* PLLVCOPD */
+ PLL0_PWR_OFF | PLL0_VCOPD_ASSERT);
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */
+ DP_PADCTL_PAD_CAL_PD_POWERDOWN);
+
+ /* step 2 */
+ ret = tegra_dc_sor_io_set_dpd(sor, 1);
+ if (ret)
+ return ret;
+ udelay(15);
+
+ /* step 3 */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
+ PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
+ udelay(25);
+
+ /* step 4 */
+ tegra_sor_write_field(sor, PLL0,
+ PLL0_PWR_MASK | /* PDPLL */
+ PLL0_VCOPD_MASK, /* PLLVCOPD */
+ PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
+ /* PLLCAPD */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
+ udelay(225);
+
+ /* step 5 PDPORT */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX7_PORT_POWERDOWN_MASK,
+ PLL2_AUX7_PORT_POWERDOWN_DISABLE);
+
+ sor->power_is_up = 1;
+
+ return 0;
+}
+
+#if DEBUG_SOR
+static void dump_sor_reg(struct tegra_dc_sor_data *sor)
+{
+#define DUMP_REG(a) printk(BIOS_INFO, "%-32s %03x %08x\n", \
+ #a, a, tegra_sor_readl(sor, a));
+
+ DUMP_REG(SUPER_STATE0);
+ DUMP_REG(SUPER_STATE1);
+ DUMP_REG(STATE0);
+ DUMP_REG(STATE1);
+ DUMP_REG(NV_HEAD_STATE0(0));
+ DUMP_REG(NV_HEAD_STATE0(1));
+ DUMP_REG(NV_HEAD_STATE1(0));
+ DUMP_REG(NV_HEAD_STATE1(1));
+ DUMP_REG(NV_HEAD_STATE2(0));
+ DUMP_REG(NV_HEAD_STATE2(1));
+ DUMP_REG(NV_HEAD_STATE3(0));
+ DUMP_REG(NV_HEAD_STATE3(1));
+ DUMP_REG(NV_HEAD_STATE4(0));
+ DUMP_REG(NV_HEAD_STATE4(1));
+ DUMP_REG(NV_HEAD_STATE5(0));
+ DUMP_REG(NV_HEAD_STATE5(1));
+ DUMP_REG(CRC_CNTRL);
+ DUMP_REG(CLK_CNTRL);
+ DUMP_REG(CAP);
+ DUMP_REG(PWR);
+ DUMP_REG(TEST);
+ DUMP_REG(PLL0);
+ DUMP_REG(PLL1);
+ DUMP_REG(PLL2);
+ DUMP_REG(PLL3);
+ DUMP_REG(CSTM);
+ DUMP_REG(LVDS);
+ DUMP_REG(CRCA);
+ DUMP_REG(CRCB);
+ DUMP_REG(SEQ_CTL);
+ DUMP_REG(LANE_SEQ_CTL);
+ DUMP_REG(SEQ_INST(0));
+ DUMP_REG(SEQ_INST(1));
+ DUMP_REG(SEQ_INST(2));
+ DUMP_REG(SEQ_INST(3));
+ DUMP_REG(SEQ_INST(4));
+ DUMP_REG(SEQ_INST(5));
+ DUMP_REG(SEQ_INST(6));
+ DUMP_REG(SEQ_INST(7));
+ DUMP_REG(SEQ_INST(8));
+ DUMP_REG(PWM_DIV);
+ DUMP_REG(PWM_CTL);
+ DUMP_REG(MSCHECK);
+ DUMP_REG(XBAR_CTRL);
+ DUMP_REG(DP_LINKCTL(0));
+ DUMP_REG(DP_LINKCTL(1));
+ DUMP_REG(DC(0));
+ DUMP_REG(DC(1));
+ DUMP_REG(LANE_DRIVE_CURRENT(0));
+ DUMP_REG(PR(0));
+ DUMP_REG(LANE4_PREEMPHASIS(0));
+ DUMP_REG(POSTCURSOR(0));
+ DUMP_REG(DP_CONFIG(0));
+ DUMP_REG(DP_CONFIG(1));
+ DUMP_REG(DP_MN(0));
+ DUMP_REG(DP_MN(1));
+ DUMP_REG(DP_PADCTL(0));
+ DUMP_REG(DP_PADCTL(1));
+ DUMP_REG(DP_DEBUG(0));
+ DUMP_REG(DP_DEBUG(1));
+ DUMP_REG(DP_SPARE(0));
+ DUMP_REG(DP_SPARE(1));
+ DUMP_REG(DP_TPG);
+
+ return;
+}
+#endif
+
+static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor,
+ int is_lvds,
+ const struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing)
+{
+ const int head_num = 0;
+ u32 reg_val = STATE1_ASY_OWNER_HEAD0 << head_num;
+ u32 vtotal, htotal;
+ u32 vsync_end, hsync_end;
+ u32 vblank_end, hblank_end;
+ u32 vblank_start, hblank_start;
+
+ reg_val |= is_lvds ? STATE1_ASY_PROTOCOL_LVDS_CUSTOM :
+ STATE1_ASY_PROTOCOL_DP_A;
+ reg_val |= STATE1_ASY_SUBOWNER_NONE |
+ STATE1_ASY_CRCMODE_COMPLETE_RASTER;
+
+ reg_val |= STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE;
+ reg_val |= STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE;
+ reg_val |= (link_cfg->bits_per_pixel > 18) ?
+ STATE1_ASY_PIXELDEPTH_BPP_24_444 :
+ STATE1_ASY_PIXELDEPTH_BPP_18_444;
+
+ tegra_sor_writel(sor, STATE1, reg_val);
+
+ /*
+ * Skipping programming NV_HEAD_STATE0, assuming:
+ * interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB
+ */
+ vtotal = timing->vsync_len.typ + timing->vback_porch.typ +
+ timing->vactive.typ + timing->vfront_porch.typ;
+ htotal = timing->hsync_len.typ + timing->hback_porch.typ +
+ timing->hactive.typ + timing->hfront_porch.typ;
+
+ tegra_sor_writel(sor, NV_HEAD_STATE1(head_num),
+ vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT |
+ htotal << NV_HEAD_STATE1_HTOTAL_SHIFT);
+
+ vsync_end = timing->vsync_len.typ - 1;
+ hsync_end = timing->hsync_len.typ - 1;
+ tegra_sor_writel(sor, NV_HEAD_STATE2(head_num),
+ vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT |
+ hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT);
+
+ vblank_end = vsync_end + timing->vback_porch.typ;
+ hblank_end = hsync_end + timing->hback_porch.typ;
+ tegra_sor_writel(sor, NV_HEAD_STATE3(head_num),
+ vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT |
+ hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT);
+
+ vblank_start = vblank_end + timing->vactive.typ;
+ hblank_start = hblank_end + timing->hactive.typ;
+ tegra_sor_writel(sor, NV_HEAD_STATE4(head_num),
+ vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT |
+ hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT);
+
+ /* TODO: adding interlace mode support */
+ tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1);
+
+ tegra_sor_write_field(sor, CSTM,
+ CSTM_ROTCLK_DEFAULT_MASK |
+ CSTM_LVDS_EN_ENABLE,
+ 2 << CSTM_ROTCLK_SHIFT |
+ is_lvds ? CSTM_LVDS_EN_ENABLE :
+ CSTM_LVDS_EN_DISABLE);
+
+ tegra_dc_sor_config_pwm(sor, 1024, 1024);
+}
+
+static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl)
+{
+ u32 reg_val = readl(&disp_ctrl->cmd.state_access);
+
+ writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+ writel(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt);
+
+ /* Enable DC now - otherwise pure text console may not show. */
+ writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
+ &disp_ctrl->cmd.disp_cmd);
+ writel(reg_val, &disp_ctrl->cmd.state_access);
+}
+
+int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ int ret;
+
+ tegra_sor_write_field(sor, CLK_CNTRL,
+ CLK_CNTRL_DP_CLK_SEL_MASK,
+ CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK);
+
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
+ PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
+ udelay(25);
+
+ tegra_sor_write_field(sor, PLL3,
+ PLL3_PLLVDD_MODE_MASK,
+ PLL3_PLLVDD_MODE_V3_3);
+ tegra_sor_writel(sor, PLL0,
+ 0xf << PLL0_ICHPMP_SHFIT |
+ 0x3 << PLL0_VCOCAP_SHIFT |
+ PLL0_PLLREG_LEVEL_V45 |
+ PLL0_RESISTORSEL_EXT |
+ PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX1_SEQ_MASK |
+ PLL2_AUX9_LVDSEN_OVERRIDE |
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
+ PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE |
+ PLL2_AUX9_LVDSEN_OVERRIDE |
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
+ tegra_sor_writel(sor, PLL1, PLL1_TERM_COMPOUT_HIGH |
+ PLL1_TMDS_TERM_ENABLE);
+
+ if (tegra_dc_sor_poll_register(sor, PLL2,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ printf("DP failed to lock PLL\n");
+ return -EIO;
+ }
+
+ tegra_sor_write_field(sor, PLL2, PLL2_AUX2_MASK |
+ PLL2_AUX7_PORT_POWERDOWN_MASK,
+ PLL2_AUX2_OVERRIDE_POWERDOWN |
+ PLL2_AUX7_PORT_POWERDOWN_DISABLE);
+
+ ret = tegra_dc_sor_power_up(sor, 0);
+ if (ret) {
+ debug("DP failed to power up\n");
+ return ret;
+ }
+
+ /* re-enable SOR clock */
+ clock_sor_enable_edp_clock();
+
+ /* Power up lanes */
+ tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1);
+
+ tegra_dc_sor_set_dp_mode(sor, link_cfg);
+ debug("%s ret\n", __func__);
+
+ return 0;
+}
+
+int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing)
+{
+ const void *blob = gd->fdt_blob;
+ struct dc_ctlr *disp_ctrl;
+ u32 reg_val;
+ int node;
+
+ /* Use the first display controller */
+ debug("%s\n", __func__);
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+ if (node < 0)
+ return -ENOENT;
+ disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+
+ tegra_dc_sor_enable_dc(disp_ctrl);
+ tegra_dc_sor_config_panel(sor, 0, link_cfg, timing);
+
+ writel(0x9f00, &disp_ctrl->cmd.state_ctrl);
+ writel(0x9f, &disp_ctrl->cmd.state_ctrl);
+
+ writel(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+ PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
+ &disp_ctrl->cmd.disp_pow_ctrl);
+
+ reg_val = tegra_sor_readl(sor, TEST);
+ if (reg_val & TEST_ATTACHED_TRUE)
+ return -EEXIST;
+
+ tegra_sor_writel(sor, SUPER_STATE1,
+ SUPER_STATE1_ATTACHED_NO);
+
+ /*
+ * Enable display2sor clock at least 2 cycles before DC start,
+ * to clear sor internal valid signal.
+ */
+ writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+ writel(0, &disp_ctrl->disp.disp_win_opt);
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+
+ /* Attach head */
+ tegra_dc_sor_update(sor);
+ tegra_sor_writel(sor, SUPER_STATE1,
+ SUPER_STATE1_ATTACHED_YES);
+ tegra_sor_writel(sor, SUPER_STATE1,
+ SUPER_STATE1_ATTACHED_YES |
+ SUPER_STATE1_ASY_HEAD_OP_AWAKE |
+ SUPER_STATE1_ASY_ORMODE_NORMAL);
+ tegra_dc_sor_super_update(sor);
+
+ /* Enable dc */
+ reg_val = readl(&disp_ctrl->cmd.state_access);
+ writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+ writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
+ &disp_ctrl->cmd.disp_cmd);
+ writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
+ writel(reg_val, &disp_ctrl->cmd.state_access);
+
+ if (tegra_dc_sor_poll_register(sor, TEST,
+ TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
+ TEST_ACT_HEAD_OPMODE_AWAKE,
+ 100,
+ TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
+ printf("dc timeout waiting for OPMOD = AWAKE\n");
+ return -ETIMEDOUT;
+ } else {
+ debug("%s: sor is attached\n", __func__);
+ }
+
+#if DEBUG_SOR
+ dump_sor_reg(sor);
+#endif
+ debug("%s: ret=%d\n", __func__, 0);
+
+ return 0;
+}
+
+void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
+ link_cfg->drive_current);
+ tegra_sor_writel(sor, PR(sor->portnum),
+ link_cfg->preemphasis);
+ tegra_sor_writel(sor, POSTCURSOR(sor->portnum),
+ link_cfg->postcursor);
+ tegra_sor_writel(sor, LVDS, 0);
+
+ tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+ tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
+
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ DP_PADCTL_TX_PU_ENABLE |
+ DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK,
+ DP_PADCTL_TX_PU_ENABLE |
+ 2 << DP_PADCTL_TX_PU_VALUE_SHIFT);
+
+ /* Precharge */
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0xf0);
+ udelay(20);
+
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
+}
+
+int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 drive_current = 0;
+ u32 pre_emphasis = 0;
+
+ /* Set to a known-good pre-calibrated setting */
+ switch (link_cfg->link_bw) {
+ case SOR_LINK_SPEED_G1_62:
+ case SOR_LINK_SPEED_G2_7:
+ drive_current = 0x13131313;
+ pre_emphasis = 0;
+ break;
+ case SOR_LINK_SPEED_G5_4:
+ debug("T124 does not support 5.4G link clock.\n");
+ default:
+ debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
+ return -ENOLINK;
+ }
+
+ tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current);
+ tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
+
+ return 0;
+}
+
+void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 pad_ctrl = 0;
+ int err = 0;
+
+ switch (link_cfg->lane_count) {
+ case 4:
+ pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
+ DP_PADCTL_PD_TXD_1_NO |
+ DP_PADCTL_PD_TXD_2_NO |
+ DP_PADCTL_PD_TXD_3_NO;
+ break;
+ case 2:
+ pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
+ DP_PADCTL_PD_TXD_1_NO |
+ DP_PADCTL_PD_TXD_2_YES |
+ DP_PADCTL_PD_TXD_3_YES;
+ break;
+ case 1:
+ pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
+ DP_PADCTL_PD_TXD_1_YES |
+ DP_PADCTL_PD_TXD_2_YES |
+ DP_PADCTL_PD_TXD_3_YES;
+ break;
+ default:
+ printf("Invalid sor lane count: %u\n", link_cfg->lane_count);
+ return;
+ }
+
+ pad_ctrl |= DP_PADCTL_PAD_CAL_PD_POWERDOWN;
+ tegra_sor_writel(sor, DP_PADCTL(sor->portnum), pad_ctrl);
+
+ err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0);
+ if (err) {
+ debug("Wait for lane power down failed: %d\n", err);
+ return;
+ }
+}
+
+int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 val = 0;
+
+ switch (cfg->lane_count) {
+ case 4:
+ val |= (DP_PADCTL_PD_TXD_3_NO |
+ DP_PADCTL_PD_TXD_2_NO);
+ /* fall through */
+ case 2:
+ val |= DP_PADCTL_PD_TXD_1_NO;
+ /* fall through */
+ case 1:
+ val |= DP_PADCTL_PD_TXD_0_NO;
+ break;
+ default:
+ debug("dp: invalid lane number %d\n", cfg->lane_count);
+ return -EINVAL;
+ }
+
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+ (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT));
+ udelay(100);
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+ 0);
+
+ return 0;
+}
+
+static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
+{
+ u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt);
+
+ reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE;
+ writel(reg_val, &disp_ctrl->disp.disp_win_opt);
+}
+
+int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor)
+{
+ int dc_reg_ctx[DC_REG_SAVE_SPACE];
+ const void *blob = gd->fdt_blob;
+ struct dc_ctlr *disp_ctrl;
+ unsigned long dc_int_mask;
+ int node;
+ int ret;
+
+ debug("%s\n", __func__);
+ /* Use the first display controller */
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+ if (node < 0) {
+ ret = -ENOENT;
+ goto err;
+ }
+ disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+
+ /* Sleep mode */
+ tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+ SUPER_STATE1_ASY_ORMODE_SAFE |
+ SUPER_STATE1_ATTACHED_YES);
+ tegra_dc_sor_super_update(sor);
+
+ tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx);
+
+ if (tegra_dc_sor_poll_register(sor, TEST,
+ TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
+ TEST_ACT_HEAD_OPMODE_SLEEP, 100,
+ TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
+ debug("dc timeout waiting for OPMOD = SLEEP\n");
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+ SUPER_STATE1_ASY_ORMODE_SAFE |
+ SUPER_STATE1_ATTACHED_NO);
+
+ /* Mask DC interrupts during the 2 dummy frames required for detach */
+ dc_int_mask = readl(&disp_ctrl->cmd.int_mask);
+ writel(0, &disp_ctrl->cmd.int_mask);
+
+ /* Stop DC->SOR path */
+ tegra_dc_sor_enable_sor(disp_ctrl, false);
+ ret = tegra_dc_sor_general_act(disp_ctrl);
+ if (ret)
+ goto err;
+
+ /* Stop DC */
+ writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd);
+ ret = tegra_dc_sor_general_act(disp_ctrl);
+ if (ret)
+ goto err;
+
+ tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx);
+
+ writel(dc_int_mask, &disp_ctrl->cmd.int_mask);
+
+ return 0;
+err:
+ debug("%s: ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp)
+{
+ const void *blob = gd->fdt_blob;
+ struct tegra_dc_sor_data *sor;
+ int node;
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
+ if (node < 0)
+ return -ENOENT;
+ sor = calloc(1, sizeof(*sor));
+ if (!sor)
+ return -ENOMEM;
+ sor->base = (void *)fdtdec_get_addr(blob, node, "reg");
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_PMC);
+ if (node < 0)
+ return -ENOENT;
+ sor->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
+
+ sor->power_is_up = 0;
+ sor->portnum = 0;
+ *sorp = sor;
+
+ return 0;
+}
diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra124/sor.h
new file mode 100644
index 0000000..dc8fd03
--- /dev/null
+++ b/drivers/video/tegra124/sor.h
@@ -0,0 +1,922 @@
+/*
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _VIDEO_TEGRA124_SOR_H
+#define _VIDEO_TEGRA124_SOR_H
+
+#define SUPER_STATE0 0x1
+#define SUPER_STATE0_UPDATE_SHIFT 0
+#define SUPER_STATE0_UPDATE_DEFAULT_MASK 0x1
+#define SUPER_STATE1 0x2
+#define SUPER_STATE1_ATTACHED_SHIFT 3
+#define SUPER_STATE1_ATTACHED_NO (0 << 3)
+#define SUPER_STATE1_ATTACHED_YES (1 << 3)
+#define SUPER_STATE1_ASY_ORMODE_SHIFT 2
+#define SUPER_STATE1_ASY_ORMODE_SAFE (0 << 2)
+#define SUPER_STATE1_ASY_ORMODE_NORMAL (1 << 2)
+#define SUPER_STATE1_ASY_HEAD_OP_SHIFT 0
+#define SUPER_STATE1_ASY_HEAD_OP_DEFAULT_MASK 0x3
+#define SUPER_STATE1_ASY_HEAD_OP_SLEEP 0
+#define SUPER_STATE1_ASY_HEAD_OP_SNOOZE 1
+#define SUPER_STATE1_ASY_HEAD_OP_AWAKE 2
+#define STATE0 0x3
+#define STATE0_UPDATE_SHIFT 0
+#define STATE0_UPDATE_DEFAULT_MASK 0x1
+#define STATE1 0x4
+#define STATE1_ASY_PIXELDEPTH_SHIFT 17
+#define STATE1_ASY_PIXELDEPTH_DEFAULT_MASK (0xf << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_16_422 (1 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_18_444 (2 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_20_422 (3 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_24_422 (4 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_24_444 (5 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_30_444 (6 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_32_422 (7 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_36_444 (8 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_48_444 (9 << 17)
+#define STATE1_ASY_REPLICATE_SHIFT 15
+#define STATE1_ASY_REPLICATE_DEFAULT_MASK (3 << 15)
+#define STATE1_ASY_REPLICATE_OFF (0 << 15)
+#define STATE1_ASY_REPLICATE_X2 (1 << 15)
+#define STATE1_ASY_REPLICATE_X4 (2 << 15)
+#define STATE1_ASY_DEPOL_SHIFT 14
+#define STATE1_ASY_DEPOL_DEFAULT_MASK (1 << 14)
+#define STATE1_ASY_DEPOL_POSITIVE_TRUE (0 << 14)
+#define STATE1_ASY_DEPOL_NEGATIVE_TRUE (1 << 14)
+#define STATE1_ASY_VSYNCPOL_SHIFT 13
+#define STATE1_ASY_VSYNCPOL_DEFAULT_MASK (1 << 13)
+#define STATE1_ASY_VSYNCPOL_POSITIVE_TRUE (0 << 13)
+#define STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE (1 << 13)
+#define STATE1_ASY_HSYNCPOL_SHIFT 12
+#define STATE1_ASY_HSYNCPOL_DEFAULT_MASK (1 << 12)
+#define STATE1_ASY_HSYNCPOL_POSITIVE_TRUE (0 << 12)
+#define STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE (1 << 12)
+#define STATE1_ASY_PROTOCOL_SHIFT 8
+#define STATE1_ASY_PROTOCOL_DEFAULT_MASK (0xf << 8)
+#define STATE1_ASY_PROTOCOL_LVDS_CUSTOM (0 << 8)
+#define STATE1_ASY_PROTOCOL_DP_A (8 << 8)
+#define STATE1_ASY_PROTOCOL_DP_B (9 << 8)
+#define STATE1_ASY_PROTOCOL_CUSTOM (15 << 8)
+#define STATE1_ASY_CRCMODE_SHIFT 6
+#define STATE1_ASY_CRCMODE_DEFAULT_MASK (3 << 6)
+#define STATE1_ASY_CRCMODE_ACTIVE_RASTER (0 << 6)
+#define STATE1_ASY_CRCMODE_COMPLETE_RASTER (1 << 6)
+#define STATE1_ASY_CRCMODE_NON_ACTIVE_RASTER (2 << 6)
+#define STATE1_ASY_SUBOWNER_SHIFT 4
+#define STATE1_ASY_SUBOWNER_DEFAULT_MASK (3 << 4)
+#define STATE1_ASY_SUBOWNER_NONE (0 << 4)
+#define STATE1_ASY_SUBOWNER_SUBHEAD0 (1 << 4)
+#define STATE1_ASY_SUBOWNER_SUBHEAD1 (2 << 4)
+#define STATE1_ASY_SUBOWNER_BOTH (3 << 4)
+#define STATE1_ASY_OWNER_SHIFT 0
+#define STATE1_ASY_OWNER_DEFAULT_MASK 0xf
+#define STATE1_ASY_OWNER_NONE 0
+#define STATE1_ASY_OWNER_HEAD0 1
+#define STATE1_ASY_OWNER_HEAD1 2
+#define NV_HEAD_STATE0(i) 0x5
+#define NV_HEAD_STATE0_INTERLACED_SHIFT 4
+#define NV_HEAD_STATE0_INTERLACED_DEFAULT_MASK (3 << 4)
+#define NV_HEAD_STATE0_INTERLACED_PROGRESSIVE (0 << 4)
+#define NV_HEAD_STATE0_INTERLACED_INTERLACED (1 << 4)
+#define NV_HEAD_STATE0_RANGECOMPRESS_SHIFT 3
+#define NV_HEAD_STATE0_RANGECOMPRESS_DEFAULT_MASK (1 << 3)
+#define NV_HEAD_STATE0_RANGECOMPRESS_DISABLE (0 << 3)
+#define NV_HEAD_STATE0_RANGECOMPRESS_ENABLE (1 << 3)
+#define NV_HEAD_STATE0_DYNRANGE_SHIFT 2
+#define NV_HEAD_STATE0_DYNRANGE_DEFAULT_MASK (1 << 2)
+#define NV_HEAD_STATE0_DYNRANGE_VESA (0 << 2)
+#define NV_HEAD_STATE0_DYNRANGE_CEA (1 << 2)
+#define NV_HEAD_STATE0_COLORSPACE_SHIFT 0
+#define NV_HEAD_STATE0_COLORSPACE_DEFAULT_MASK 0x3
+#define NV_HEAD_STATE0_COLORSPACE_RGB 0
+#define NV_HEAD_STATE0_COLORSPACE_YUV_601 1
+#define NV_HEAD_STATE0_COLORSPACE_YUV_709 2
+#define NV_HEAD_STATE1(i) (7 + i)
+#define NV_HEAD_STATE1_VTOTAL_SHIFT 16
+#define NV_HEAD_STATE1_VTOTAL_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE1_HTOTAL_SHIFT 0
+#define NV_HEAD_STATE1_HTOTAL_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE2(i) (9 + i)
+#define NV_HEAD_STATE2_VSYNC_END_SHIFT 16
+#define NV_HEAD_STATE2_VSYNC_END_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE2_HSYNC_END_SHIFT 0
+#define NV_HEAD_STATE2_HSYNC_END_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE3(i) (0xb + i)
+#define NV_HEAD_STATE3_VBLANK_END_SHIFT 16
+#define NV_HEAD_STATE3_VBLANK_END_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE3_HBLANK_END_SHIFT 0
+#define NV_HEAD_STATE3_HBLANK_END_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE4(i) (0xd + i)
+#define NV_HEAD_STATE4_VBLANK_START_SHIFT 16
+#define NV_HEAD_STATE4_VBLANK_START_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE4_HBLANK_START_SHIFT 0
+#define NV_HEAD_STATE4_HBLANK_START_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE5(i) (0xf + i)
+#define CRC_CNTRL 0x11
+#define CRC_CNTRL_ARM_CRC_ENABLE_SHIFT 0
+#define CRC_CNTRL_ARM_CRC_ENABLE_NO 0
+#define CRC_CNTRL_ARM_CRC_ENABLE_YES 1
+#define CRC_CNTRL_ARM_CRC_ENABLE_DIS 0
+#define CRC_CNTRL_ARM_CRC_ENABLE_EN 1
+#define CLK_CNTRL 0x13
+#define CLK_CNTRL_DP_CLK_SEL_SHIFT 0
+#define CLK_CNTRL_DP_CLK_SEL_MASK 0x3
+#define CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK 0
+#define CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK 1
+#define CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK 2
+#define CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK 3
+#define CLK_CNTRL_DP_LINK_SPEED_SHIFT 2
+#define CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2)
+#define CLK_CNTRL_DP_LINK_SPEED_G1_62 (6 << 2)
+#define CLK_CNTRL_DP_LINK_SPEED_G2_7 (10 << 2)
+#define CLK_CNTRL_DP_LINK_SPEED_LVDS (7 << 2)
+#define CAP 0x14
+#define CAP_DP_A_SHIFT 24
+#define CAP_DP_A_DEFAULT_MASK (1 << 24)
+#define CAP_DP_A_FALSE (0 << 24)
+#define CAP_DP_A_TRUE (1 << 24)
+#define CAP_DP_B_SHIFT 25
+#define CAP_DP_B_DEFAULT_MASK (1 << 24)
+#define CAP_DP_B_FALSE (0 << 24)
+#define CAP_DP_B_TRUE (1 << 24)
+#define PWR 0x15
+#define PWR_SETTING_NEW_SHIFT 31
+#define PWR_SETTING_NEW_DEFAULT_MASK (1 << 31)
+#define PWR_SETTING_NEW_DONE (0 << 31)
+#define PWR_SETTING_NEW_PENDING (1 << 31)
+#define PWR_SETTING_NEW_TRIGGER (1 << 31)
+#define PWR_MODE_SHIFT 28
+#define PWR_MODE_DEFAULT_MASK (1 << 28)
+#define PWR_MODE_NORMAL (0 << 28)
+#define PWR_MODE_SAFE (1 << 28)
+#define PWR_HALT_DELAY_SHIFT 24
+#define PWR_HALT_DELAY_DEFAULT_MASK (1 << 24)
+#define PWR_HALT_DELAY_DONE (0 << 24)
+#define PWR_HALT_DELAY_ACTIVE (1 << 24)
+#define PWR_SAFE_START_SHIFT 17
+#define PWR_SAFE_START_DEFAULT_MASK (1 << 17)
+#define PWR_SAFE_START_NORMAL (0 << 17)
+#define PWR_SAFE_START_ALT (1 << 17)
+#define PWR_SAFE_STATE_SHIFT 16
+#define PWR_SAFE_STATE_DEFAULT_MASK (1 << 16)
+#define PWR_SAFE_STATE_PD (0 << 16)
+#define PWR_SAFE_STATE_PU (1 << 16)
+#define PWR_NORMAL_START_SHIFT 1
+#define PWR_NORMAL_START_DEFAULT_MASK (1 << 1)
+#define PWR_NORMAL_START_NORMAL (0 << 16)
+#define PWR_NORMAL_START_ALT (1 << 16)
+#define PWR_NORMAL_STATE_SHIFT 0
+#define PWR_NORMAL_STATE_DEFAULT_MASK 0x1
+#define PWR_NORMAL_STATE_PD 0
+#define PWR_NORMAL_STATE_PU 1
+#define TEST 0x16
+#define TEST_TESTMUX_SHIFT 24
+#define TEST_TESTMUX_DEFAULT_MASK (0xff << 24)
+#define TEST_TESTMUX_AVSS (0 << 24)
+#define TEST_TESTMUX_CLOCKIN (2 << 24)
+#define TEST_TESTMUX_PLL_VOL (4 << 24)
+#define TEST_TESTMUX_SLOWCLKINT (8 << 24)
+#define TEST_TESTMUX_AVDD (16 << 24)
+#define TEST_TESTMUX_VDDREG (32 << 24)
+#define TEST_TESTMUX_REGREF_VDDREG (64 << 24)
+#define TEST_TESTMUX_REGREF_AVDD (128 << 24)
+#define TEST_CRC_SHIFT 23
+#define TEST_CRC_PRE_SERIALIZE (0 << 23)
+#define TEST_CRC_POST_DESERIALIZE (1 << 23)
+#define TEST_TPAT_SHIFT 20
+#define TEST_TPAT_DEFAULT_MASK (7 << 20)
+#define TEST_TPAT_LO (0 << 20)
+#define TEST_TPAT_TDAT (1 << 20)
+#define TEST_TPAT_RAMP (2 << 20)
+#define TEST_TPAT_WALK (3 << 20)
+#define TEST_TPAT_MAXSTEP (4 << 20)
+#define TEST_TPAT_MINSTEP (5 << 20)
+#define TEST_DSRC_SHIFT 16
+#define TEST_DSRC_DEFAULT_MASK (3 << 16)
+#define TEST_DSRC_NORMAL (0 << 16)
+#define TEST_DSRC_DEBUG (1 << 16)
+#define TEST_DSRC_TGEN (2 << 16)
+#define TEST_HEAD_NUMBER_SHIFT 12
+#define TEST_HEAD_NUMBER_DEFAULT_MASK (3 << 12)
+#define TEST_HEAD_NUMBER_NONE (0 << 12)
+#define TEST_HEAD_NUMBER_HEAD0 (1 << 12)
+#define TEST_HEAD_NUMBER_HEAD1 (2 << 12)
+#define TEST_ATTACHED_SHIFT 10
+#define TEST_ATTACHED_DEFAULT_MASK (1 << 10)
+#define TEST_ATTACHED_FALSE (0 << 10)
+#define TEST_ATTACHED_TRUE (1 << 10)
+#define TEST_ACT_HEAD_OPMODE_SHIFT 8
+#define TEST_ACT_HEAD_OPMODE_DEFAULT_MASK (3 << 8)
+#define TEST_ACT_HEAD_OPMODE_SLEEP (0 << 8)
+#define TEST_ACT_HEAD_OPMODE_SNOOZE (1 << 8)
+#define TEST_ACT_HEAD_OPMODE_AWAKE (2 << 8)
+#define TEST_INVD_SHIFT 6
+#define TEST_INVD_DISABLE (0 << 6)
+#define TEST_INVD_ENABLE (1 << 6)
+#define TEST_TEST_ENABLE_SHIFT 1
+#define TEST_TEST_ENABLE_DISABLE (0 << 1)
+#define TEST_TEST_ENABLE_ENABLE (1 << 1)
+#define PLL0 0x17
+#define PLL0_ICHPMP_SHFIT 24
+#define PLL0_ICHPMP_DEFAULT_MASK (0xf << 24)
+#define PLL0_VCOCAP_SHIFT 8
+#define PLL0_VCOCAP_DEFAULT_MASK (0xf << 8)
+#define PLL0_PLLREG_LEVEL_SHIFT 6
+#define PLL0_PLLREG_LEVEL_DEFAULT_MASK (3 << 6)
+#define PLL0_PLLREG_LEVEL_V25 (0 << 6)
+#define PLL0_PLLREG_LEVEL_V15 (1 << 6)
+#define PLL0_PLLREG_LEVEL_V35 (2 << 6)
+#define PLL0_PLLREG_LEVEL_V45 (3 << 6)
+#define PLL0_PULLDOWN_SHIFT 5
+#define PLL0_PULLDOWN_DEFAULT_MASK (1 << 5)
+#define PLL0_PULLDOWN_DISABLE (0 << 5)
+#define PLL0_PULLDOWN_ENABLE (1 << 5)
+#define PLL0_RESISTORSEL_SHIFT 4
+#define PLL0_RESISTORSEL_DEFAULT_MASK (1 << 4)
+#define PLL0_RESISTORSEL_INT (0 << 4)
+#define PLL0_RESISTORSEL_EXT (1 << 4)
+#define PLL0_VCOPD_SHIFT 2
+#define PLL0_VCOPD_MASK (1 << 2)
+#define PLL0_VCOPD_RESCIND (0 << 2)
+#define PLL0_VCOPD_ASSERT (1 << 2)
+#define PLL0_PWR_SHIFT 0
+#define PLL0_PWR_MASK 1
+#define PLL0_PWR_ON 0
+#define PLL0_PWR_OFF 1
+#define PLL1_TMDS_TERM_SHIFT 8
+#define PLL1_TMDS_TERM_DISABLE (0 << 8)
+#define PLL1_TMDS_TERM_ENABLE (1 << 8)
+#define PLL1 0x18
+#define PLL1_TERM_COMPOUT_SHIFT 15
+#define PLL1_TERM_COMPOUT_LOW (0 << 15)
+#define PLL1_TERM_COMPOUT_HIGH (1 << 15)
+#define PLL2 0x19
+#define PLL2_DCIR_PLL_RESET_SHIFT 0
+#define PLL2_DCIR_PLL_RESET_OVERRIDE (0 << 0)
+#define PLL2_DCIR_PLL_RESET_ALLOW (1 << 0)
+#define PLL2_AUX1_SHIFT 17
+#define PLL2_AUX1_SEQ_MASK (1 << 17)
+#define PLL2_AUX1_SEQ_PLLCAPPD_ALLOW (0 << 17)
+#define PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE (1 << 17)
+#define PLL2_AUX2_SHIFT 18
+#define PLL2_AUX2_MASK (1 << 18)
+#define PLL2_AUX2_OVERRIDE_POWERDOWN (0 << 18)
+#define PLL2_AUX2_ALLOW_POWERDOWN (1 << 18)
+#define PLL2_AUX6_SHIFT 22
+#define PLL2_AUX6_BANDGAP_POWERDOWN_MASK (1 << 22)
+#define PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE (0 << 22)
+#define PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE (1 << 22)
+#define PLL2_AUX7_SHIFT 23
+#define PLL2_AUX7_PORT_POWERDOWN_MASK (1 << 23)
+#define PLL2_AUX7_PORT_POWERDOWN_DISABLE (0 << 23)
+#define PLL2_AUX7_PORT_POWERDOWN_ENABLE (1 << 23)
+#define PLL2_AUX8_SHIFT 24
+#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK (1 << 24)
+#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE (0 << 24)
+#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE (1 << 24)
+#define PLL2_AUX9_SHIFT 25
+#define PLL2_AUX9_LVDSEN_ALLOW (0 << 25)
+#define PLL2_AUX9_LVDSEN_OVERRIDE (1 << 25)
+#define PLL3 0x1a
+#define PLL3_PLLVDD_MODE_SHIFT 13
+#define PLL3_PLLVDD_MODE_MASK (1 << 13)
+#define PLL3_PLLVDD_MODE_V1_8 (0 << 13)
+#define PLL3_PLLVDD_MODE_V3_3 (1 << 13)
+#define CSTM 0x1b
+#define CSTM_ROTDAT_SHIFT 28
+#define CSTM_ROTDAT_DEFAULT_MASK (7 << 28)
+#define CSTM_ROTCLK_SHIFT 24
+#define CSTM_ROTCLK_DEFAULT_MASK (0xf << 24)
+#define CSTM_LVDS_EN_SHIFT 16
+#define CSTM_LVDS_EN_DISABLE (0 << 16)
+#define CSTM_LVDS_EN_ENABLE (1 << 16)
+#define CSTM_LINKACTB_SHIFT 15
+#define CSTM_LINKACTB_DISABLE (0 << 15)
+#define CSTM_LINKACTB_ENABLE (1 << 15)
+#define CSTM_LINKACTA_SHIFT 14
+#define CSTM_LINKACTA_DISABLE (0 << 14)
+#define CSTM_LINKACTA_ENABLE (1 << 14)
+#define LVDS 0x1c
+#define LVDS_ROTDAT_SHIFT 28
+#define LVDS_ROTDAT_DEFAULT_MASK (7 << 28)
+#define LVDS_ROTDAT_RST (0 << 28)
+#define LVDS_ROTCLK_SHIFT 24
+#define LVDS_ROTCLK_DEFAULT_MASK (0xf << 24)
+#define LVDS_ROTCLK_RST (0 << 24)
+#define LVDS_PLLDIV_SHIFT 21
+#define LVDS_PLLDIV_DEFAULT_MASK (1 << 21)
+#define LVDS_PLLDIV_BY_7 (0 << 21)
+#define LVDS_BALANCED_SHIFT 19
+#define LVDS_BALANCED_DEFAULT_MASK (1 << 19)
+#define LVDS_BALANCED_DISABLE (0 << 19)
+#define LVDS_BALANCED_ENABLE (1 << 19)
+#define LVDS_NEW_MODE_SHIFT 18
+#define LVDS_NEW_MODE_DEFAULT_MASK (1 << 18)
+#define LVDS_NEW_MODE_DISABLE (0 << 18)
+#define LVDS_NEW_MODE_ENABLE (1 << 18)
+#define LVDS_DUP_SYNC_SHIFT 17
+#define LVDS_DUP_SYNC_DEFAULT_MASK (1 << 17)
+#define LVDS_DUP_SYNC_DISABLE (0 << 17)
+#define LVDS_DUP_SYNC_ENABLE (1 << 17)
+#define LVDS_LVDS_EN_SHIFT 16
+#define LVDS_LVDS_EN_DEFAULT_MASK (1 << 16)
+#define LVDS_LVDS_EN_ENABLE (1 << 16)
+#define LVDS_LINKACTB_SHIFT 15
+#define LVDS_LINKACTB_DEFAULT_MASK (1 << 15)
+#define LVDS_LINKACTB_DISABLE (0 << 15)
+#define LVDS_LINKACTB_ENABLE (1 << 15)
+#define LVDS_LINKACTA_SHIFT 14
+#define LVDS_LINKACTA_DEFAULT_MASK (1 << 14)
+#define LVDS_LINKACTA_ENABLE (1 << 14)
+#define LVDS_MODE_SHIFT 12
+#define LVDS_MODE_DEFAULT_MASK (3 << 12)
+#define LVDS_MODE_LVDS (0 << 12)
+#define LVDS_UPPER_SHIFT 11
+#define LVDS_UPPER_DEFAULT_MASK (1 << 11)
+#define LVDS_UPPER_FALSE (0 << 11)
+#define LVDS_UPPER_TRUE (1 << 11)
+#define LVDS_PD_TXCB_SHIFT 9
+#define LVDS_PD_TXCB_DEFAULT_MASK (1 << 9)
+#define LVDS_PD_TXCB_ENABLE (0 << 9)
+#define LVDS_PD_TXCB_DISABLE (1 << 9)
+#define LVDS_PD_TXCA_SHIFT 8
+#define LVDS_PD_TXCA_DEFAULT_MASK (1 << 8)
+#define LVDS_PD_TXCA_ENABLE (0 << 8)
+#define LVDS_PD_TXDB_3_SHIFT 7
+#define LVDS_PD_TXDB_3_DEFAULT_MASK (1 << 7)
+#define LVDS_PD_TXDB_3_ENABLE (0 << 7)
+#define LVDS_PD_TXDB_3_DISABLE (1 << 7)
+#define LVDS_PD_TXDB_2_SHIFT 6
+#define LVDS_PD_TXDB_2_DEFAULT_MASK (1 << 6)
+#define LVDS_PD_TXDB_2_ENABLE (0 << 6)
+#define LVDS_PD_TXDB_2_DISABLE (1 << 6)
+#define LVDS_PD_TXDB_1_SHIFT 5
+#define LVDS_PD_TXDB_1_DEFAULT_MASK (1 << 5)
+#define LVDS_PD_TXDB_1_ENABLE (0 << 5)
+#define LVDS_PD_TXDB_1_DISABLE (1 << 5)
+#define LVDS_PD_TXDB_0_SHIFT 4
+#define LVDS_PD_TXDB_0_DEFAULT_MASK (1 << 4)
+#define LVDS_PD_TXDB_0_ENABLE (0 << 4)
+#define LVDS_PD_TXDB_0_DISABLE (1 << 4)
+#define LVDS_PD_TXDA_3_SHIFT 3
+#define LVDS_PD_TXDA_3_DEFAULT_MASK (1 << 3)
+#define LVDS_PD_TXDA_3_ENABLE (0 << 3)
+#define LVDS_PD_TXDA_3_DISABLE (1 << 3)
+#define LVDS_PD_TXDA_2_SHIFT 2
+#define LVDS_PD_TXDA_2_DEFAULT_MASK (1 << 2)
+#define LVDS_PD_TXDA_2_ENABLE (0 << 2)
+#define LVDS_PD_TXDA_1_SHIFT 1
+#define LVDS_PD_TXDA_1_DEFAULT_MASK (1 << 1)
+#define LVDS_PD_TXDA_1_ENABLE (0 << 1)
+#define LVDS_PD_TXDA_0_SHIFT 0
+#define LVDS_PD_TXDA_0_DEFAULT_MASK 0x1
+#define LVDS_PD_TXDA_0_ENABLE 0
+#define CRCA 0x1d
+#define CRCA_VALID_FALSE 0
+#define CRCA_VALID_TRUE 1
+#define CRCA_VALID_RST 1
+#define CRCB 0x1e
+#define CRCB_CRC_DEFAULT_MASK 0xffffffff
+#define SEQ_CTL 0x20
+#define SEQ_CTL_SWITCH_SHIFT 30
+#define SEQ_CTL_SWITCH_MASK (1 << 30)
+#define SEQ_CTL_SWITCH_WAIT (0 << 30)
+#define SEQ_CTL_SWITCH_FORCE (1 << 30)
+#define SEQ_CTL_STATUS_SHIFT 28
+#define SEQ_CTL_STATUS_MASK (1 << 28)
+#define SEQ_CTL_STATUS_STOPPED (0 << 28)
+#define SEQ_CTL_STATUS_RUNNING (1 << 28)
+#define SEQ_CTL_PC_SHIFT 16
+#define SEQ_CTL_PC_MASK (0xf << 16)
+#define SEQ_CTL_PD_PC_ALT_SHIFT 12
+#define SEQ_CTL_PD_PC_ALT_MASK (0xf << 12)
+#define SEQ_CTL_PD_PC_SHIFT 8
+#define SEQ_CTL_PD_PC_MASK (0xf << 8)
+#define SEQ_CTL_PU_PC_ALT_SHIFT 4
+#define SEQ_CTL_PU_PC_ALT_MASK (0xf << 4)
+#define SEQ_CTL_PU_PC_SHIFT 0
+#define SEQ_CTL_PU_PC_MASK 0xf
+#define LANE_SEQ_CTL 0x21
+#define LANE_SEQ_CTL_SETTING_NEW_SHIFT 31
+#define LANE_SEQ_CTL_SETTING_MASK (1 << 31)
+#define LANE_SEQ_CTL_SETTING_NEW_DONE (0 << 31)
+#define LANE_SEQ_CTL_SETTING_NEW_PENDING (1 << 31)
+#define LANE_SEQ_CTL_SETTING_NEW_TRIGGER (1 << 31)
+#define LANE_SEQ_CTL_SEQ_STATE_SHIFT 28
+#define LANE_SEQ_CTL_SEQ_STATE_IDLE (0 << 28)
+#define LANE_SEQ_CTL_SEQ_STATE_BUSY (1 << 28)
+#define LANE_SEQ_CTL_SEQUENCE_SHIFT 20
+#define LANE_SEQ_CTL_SEQUENCE_UP (0 << 20)
+#define LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20)
+#define LANE_SEQ_CTL_NEW_POWER_STATE_SHIFT 16
+#define LANE_SEQ_CTL_NEW_POWER_STATE_PU (0 << 16)
+#define LANE_SEQ_CTL_NEW_POWER_STATE_PD (1 << 16)
+#define LANE_SEQ_CTL_DELAY_SHIFT 12
+#define LANE_SEQ_CTL_DELAY_DEFAULT_MASK (0xf << 12)
+#define LANE_SEQ_CTL_LANE9_STATE_SHIFT 9
+#define LANE_SEQ_CTL_LANE9_STATE_POWERUP (0 << 9)
+#define LANE_SEQ_CTL_LANE9_STATE_POWERDOWN (1 << 9)
+#define LANE_SEQ_CTL_LANE8_STATE_SHIFT 8
+#define LANE_SEQ_CTL_LANE8_STATE_POWERUP (0 << 8)
+#define LANE_SEQ_CTL_LANE8_STATE_POWERDOWN (1 << 8)
+#define LANE_SEQ_CTL_LANE7_STATE_SHIFT 7
+#define LANE_SEQ_CTL_LANE7_STATE_POWERUP (0 << 7)
+#define LANE_SEQ_CTL_LANE7_STATE_POWERDOWN (1 << 7)
+#define LANE_SEQ_CTL_LANE6_STATE_SHIFT 6
+#define LANE_SEQ_CTL_LANE6_STATE_POWERUP (0 << 6)
+#define LANE_SEQ_CTL_LANE6_STATE_POWERDOWN (1 << 6)
+#define LANE_SEQ_CTL_LANE5_STATE_SHIFT 5
+#define LANE_SEQ_CTL_LANE5_STATE_POWERUP (0 << 5)
+#define LANE_SEQ_CTL_LANE5_STATE_POWERDOWN (1 << 5)
+#define LANE_SEQ_CTL_LANE4_STATE_SHIFT 4
+#define LANE_SEQ_CTL_LANE4_STATE_POWERUP (0 << 4)
+#define LANE_SEQ_CTL_LANE4_STATE_POWERDOWN (1 << 4)
+#define LANE_SEQ_CTL_LANE3_STATE_SHIFT 3
+#define LANE_SEQ_CTL_LANE3_STATE_POWERUP (0 << 3)
+#define LANE_SEQ_CTL_LANE3_STATE_POWERDOWN (1 << 3)
+#define LANE_SEQ_CTL_LANE2_STATE_SHIFT 2
+#define LANE_SEQ_CTL_LANE2_STATE_POWERUP (0 << 2)
+#define LANE_SEQ_CTL_LANE2_STATE_POWERDOWN (1 << 2)
+#define LANE_SEQ_CTL_LANE1_STATE_SHIFT 1
+#define LANE_SEQ_CTL_LANE1_STATE_POWERUP (0 << 1)
+#define LANE_SEQ_CTL_LANE1_STATE_POWERDOWN (1 << 1)
+#define LANE_SEQ_CTL_LANE0_STATE_SHIFT 0
+#define LANE_SEQ_CTL_LANE0_STATE_POWERUP 0
+#define LANE_SEQ_CTL_LANE0_STATE_POWERDOWN 1
+#define SEQ_INST(i) (0x22 + i)
+#define SEQ_INST_PLL_PULLDOWN_SHIFT 31
+#define SEQ_INST_PLL_PULLDOWN_DISABLE (0 << 31)
+#define SEQ_INST_PLL_PULLDOWN_ENABLE (1 << 31)
+#define SEQ_INST_POWERDOWN_MACRO_SHIFT 30
+#define SEQ_INST_POWERDOWN_MACRO_NORMAL (0 << 30)
+#define SEQ_INST_POWERDOWN_MACRO_POWERDOWN (1 << 30)
+#define SEQ_INST_ASSERT_PLL_RESET_SHIFT 29
+#define SEQ_INST_ASSERT_PLL_RESET_NORMAL (0 << 29)
+#define SEQ_INST_ASSERT_PLL_RESET_RST (1 << 29)
+#define SEQ_INST_BLANK_V_SHIFT 28
+#define SEQ_INST_BLANK_V_NORMAL (0 << 28)
+#define SEQ_INST_BLANK_V_INACTIVE (1 << 28)
+#define SEQ_INST_BLANK_H_SHIFT 27
+#define SEQ_INST_BLANK_H_NORMAL (0 << 27)
+#define SEQ_INST_BLANK_H_INACTIVE (1 << 27)
+#define SEQ_INST_BLANK_DE_SHIFT 26
+#define SEQ_INST_BLANK_DE_NORMAL (0 << 26)
+#define SEQ_INST_BLANK_DE_INACTIVE (1 << 26)
+#define SEQ_INST_BLACK_DATA_SHIFT 25
+#define SEQ_INST_BLACK_DATA_NORMAL (0 << 25)
+#define SEQ_INST_BLACK_DATA_BLACK (1 << 25)
+#define SEQ_INST_TRISTATE_IOS_SHIFT 24
+#define SEQ_INST_TRISTATE_IOS_ENABLE_PINS (0 << 24)
+#define SEQ_INST_TRISTATE_IOS_TRISTATE (1 << 24)
+#define SEQ_INST_DRIVE_PWM_OUT_LO_SHIFT 23
+#define SEQ_INST_DRIVE_PWM_OUT_LO_FALSE (0 << 23)
+#define SEQ_INST_DRIVE_PWM_OUT_LO_TRUE (1 << 23)
+#define SEQ_INST_PIN_B_SHIFT 22
+#define SEQ_INST_PIN_B_LOW (0 << 22)
+#define SEQ_INST_PIN_B_HIGH (1 << 22)
+#define SEQ_INST_PIN_A_SHIFT 21
+#define SEQ_INST_PIN_A_LOW (0 << 21)
+#define SEQ_INST_PIN_A_HIGH (1 << 21)
+#define SEQ_INST_SEQUENCE_SHIFT 19
+#define SEQ_INST_SEQUENCE_UP (0 << 19)
+#define SEQ_INST_SEQUENCE_DOWN (1 << 19)
+#define SEQ_INST_LANE_SEQ_SHIFT 18
+#define SEQ_INST_LANE_SEQ_STOP (0 << 18)
+#define SEQ_INST_LANE_SEQ_RUN (1 << 18)
+#define SEQ_INST_PDPORT_SHIFT 17
+#define SEQ_INST_PDPORT_NO (0 << 17)
+#define SEQ_INST_PDPORT_YES (1 << 17)
+#define SEQ_INST_PDPLL_SHIFT 16
+#define SEQ_INST_PDPLL_NO (0 << 16)
+#define SEQ_INST_PDPLL_YES (1 << 16)
+#define SEQ_INST_HALT_SHIFT 15
+#define SEQ_INST_HALT_FALSE (0 << 15)
+#define SEQ_INST_HALT_TRUE (1 << 15)
+#define SEQ_INST_WAIT_UNITS_SHIFT 12
+#define SEQ_INST_WAIT_UNITS_DEFAULT_MASK (3 << 12)
+#define SEQ_INST_WAIT_UNITS_US (0 << 12)
+#define SEQ_INST_WAIT_UNITS_MS (1 << 12)
+#define SEQ_INST_WAIT_UNITS_VSYNC (2 << 12)
+#define SEQ_INST_WAIT_TIME_SHIFT 0
+#define SEQ_INST_WAIT_TIME_DEFAULT_MASK 0x3ff
+#define PWM_DIV 0x32
+#define PWM_DIV_DIVIDE_DEFAULT_MASK 0xffffff
+#define PWM_CTL 0x33
+#define PWM_CTL_SETTING_NEW_SHIFT 31
+#define PWM_CTL_SETTING_NEW_DONE (0 << 31)
+#define PWM_CTL_SETTING_NEW_PENDING (1 << 31)
+#define PWM_CTL_SETTING_NEW_TRIGGER (1 << 31)
+#define PWM_CTL_CLKSEL_SHIFT 30
+#define PWM_CTL_CLKSEL_PCLK (0 << 30)
+#define PWM_CTL_CLKSEL_XTAL (1 << 30)
+#define PWM_CTL_DUTY_CYCLE_SHIFT 0
+#define PWM_CTL_DUTY_CYCLE_MASK 0xffffff
+#define MSCHECK 0x49
+#define MSCHECK_CTL_SHIFT 31
+#define MSCHECK_CTL_CLEAR (0 << 31)
+#define MSCHECK_CTL_RUN (1 << 31)
+#define XBAR_CTRL 0x4a
+#define DP_LINKCTL(i) (0x4c + (i))
+#define DP_LINKCTL_FORCE_IDLEPTTRN_SHIFT 31
+#define DP_LINKCTL_FORCE_IDLEPTTRN_NO (0 << 31)
+#define DP_LINKCTL_FORCE_IDLEPTTRN_YES (1 << 31)
+#define DP_LINKCTL_COMPLIANCEPTTRN_SHIFT 28
+#define DP_LINKCTL_COMPLIANCEPTTRN_NOPATTERN (0 << 28)
+#define DP_LINKCTL_COMPLIANCEPTTRN_COLORSQARE (1 << 28)
+#define DP_LINKCTL_LANECOUNT_SHIFT 16
+#define DP_LINKCTL_LANECOUNT_MASK (0x1f << 16)
+#define DP_LINKCTL_LANECOUNT_ZERO (0 << 16)
+#define DP_LINKCTL_LANECOUNT_ONE (1 << 16)
+#define DP_LINKCTL_LANECOUNT_TWO (3 << 16)
+#define DP_LINKCTL_LANECOUNT_FOUR (15 << 16)
+#define DP_LINKCTL_ENHANCEDFRAME_SHIFT 14
+#define DP_LINKCTL_ENHANCEDFRAME_DISABLE (0 << 14)
+#define DP_LINKCTL_ENHANCEDFRAME_ENABLE (1 << 14)
+#define DP_LINKCTL_SYNCMODE_SHIFT 10
+#define DP_LINKCTL_SYNCMODE_DISABLE (0 << 10)
+#define DP_LINKCTL_SYNCMODE_ENABLE (1 << 10)
+#define DP_LINKCTL_TUSIZE_SHIFT 2
+#define DP_LINKCTL_TUSIZE_MASK (0x7f << 2)
+#define DP_LINKCTL_ENABLE_SHIFT 0
+#define DP_LINKCTL_ENABLE_NO 0
+#define DP_LINKCTL_ENABLE_YES 1
+#define DC(i) (0x4e + (i))
+#define DC_LANE3_DP_LANE3_SHIFT 24
+#define DC_LANE3_DP_LANE3_MASK (0xff << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL0 (17 << 24)
+#define DC_LANE3_DP_LANE3_P1_LEVEL0 (21 << 24)
+#define DC_LANE3_DP_LANE3_P2_LEVEL0 (26 << 24)
+#define DC_LANE3_DP_LANE3_P3_LEVEL0 (34 << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL1 (26 << 24)
+#define DC_LANE3_DP_LANE3_P1_LEVEL1 (32 << 24)
+#define DC_LANE3_DP_LANE3_P2_LEVEL1 (39 << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL2 (34 << 24)
+#define DC_LANE3_DP_LANE3_P1_LEVEL2 (43 << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL3 (51 << 24)
+#define DC_LANE2_DP_LANE0_SHIFT 16
+#define DC_LANE2_DP_LANE0_MASK (0xff << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL0 (17 << 16)
+#define DC_LANE2_DP_LANE0_P1_LEVEL0 (21 << 16)
+#define DC_LANE2_DP_LANE0_P2_LEVEL0 (26 << 16)
+#define DC_LANE2_DP_LANE0_P3_LEVEL0 (34 << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL1 (26 << 16)
+#define DC_LANE2_DP_LANE0_P1_LEVEL1 (32 << 16)
+#define DC_LANE2_DP_LANE0_P2_LEVEL1 (39 << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL2 (34 << 16)
+#define DC_LANE2_DP_LANE0_P1_LEVEL2 (43 << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL3 (51 << 16)
+#define DC_LANE1_DP_LANE1_SHIFT 8
+#define DC_LANE1_DP_LANE1_MASK (0xff << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL0 (17 << 8)
+#define DC_LANE1_DP_LANE1_P1_LEVEL0 (21 << 8)
+#define DC_LANE1_DP_LANE1_P2_LEVEL0 (26 << 8)
+#define DC_LANE1_DP_LANE1_P3_LEVEL0 (34 << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL1 (26 << 8)
+#define DC_LANE1_DP_LANE1_P1_LEVEL1 (32 << 8)
+#define DC_LANE1_DP_LANE1_P2_LEVEL1 (39 << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL2 (34 << 8)
+#define DC_LANE1_DP_LANE1_P1_LEVEL2 (43 << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL3 (51 << 8)
+#define DC_LANE0_DP_LANE2_SHIFT 0
+#define DC_LANE0_DP_LANE2_MASK 0xff
+#define DC_LANE0_DP_LANE2_P0_LEVEL0 17
+#define DC_LANE0_DP_LANE2_P1_LEVEL0 21
+#define DC_LANE0_DP_LANE2_P2_LEVEL0 26
+#define DC_LANE0_DP_LANE2_P3_LEVEL0 34
+#define DC_LANE0_DP_LANE2_P0_LEVEL1 26
+#define DC_LANE0_DP_LANE2_P1_LEVEL1 32
+#define DC_LANE0_DP_LANE2_P2_LEVEL1 39
+#define DC_LANE0_DP_LANE2_P0_LEVEL2 34
+#define DC_LANE0_DP_LANE2_P1_LEVEL2 43
+#define DC_LANE0_DP_LANE2_P0_LEVEL3 51
+#define LANE_DRIVE_CURRENT(i) (0x4e + (i))
+#define PR(i) (0x52 + (i))
+#define PR_LANE3_DP_LANE3_SHIFT 24
+#define PR_LANE3_DP_LANE3_MASK (0xff << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D1_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D2_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D3_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL1 (4 << 24)
+#define PR_LANE3_DP_LANE3_D1_LEVEL1 (6 << 24)
+#define PR_LANE3_DP_LANE3_D2_LEVEL1 (17 << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL2 (8 << 24)
+#define PR_LANE3_DP_LANE3_D1_LEVEL2 (13 << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL3 (17 << 24)
+#define PR_LANE2_DP_LANE0_SHIFT 16
+#define PR_LANE2_DP_LANE0_MASK (0xff << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D1_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D2_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D3_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL1 (4 << 16)
+#define PR_LANE2_DP_LANE0_D1_LEVEL1 (6 << 16)
+#define PR_LANE2_DP_LANE0_D2_LEVEL1 (17 << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL2 (8 << 16)
+#define PR_LANE2_DP_LANE0_D1_LEVEL2 (13 << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL3 (17 << 16)
+#define PR_LANE1_DP_LANE1_SHIFT 8
+#define PR_LANE1_DP_LANE1_MASK (0xff >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D1_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D2_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D3_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL1 (4 >> 8)
+#define PR_LANE1_DP_LANE1_D1_LEVEL1 (6 >> 8)
+#define PR_LANE1_DP_LANE1_D2_LEVEL1 (17 >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL2 (8 >> 8)
+#define PR_LANE1_DP_LANE1_D1_LEVEL2 (13 >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL3 (17 >> 8)
+#define PR_LANE0_DP_LANE2_SHIFT 0
+#define PR_LANE0_DP_LANE2_MASK 0xff
+#define PR_LANE0_DP_LANE2_D0_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D1_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D2_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D3_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D0_LEVEL1 4
+#define PR_LANE0_DP_LANE2_D1_LEVEL1 6
+#define PR_LANE0_DP_LANE2_D2_LEVEL1 17
+#define PR_LANE0_DP_LANE2_D0_LEVEL2 8
+#define PR_LANE0_DP_LANE2_D1_LEVEL2 13
+#define PR_LANE0_DP_LANE2_D0_LEVEL3 17
+#define LANE4_PREEMPHASIS(i) (0x54 + (i))
+#define POSTCURSOR(i) (0x56 + (i))
+#define DP_CONFIG(i) (0x58 + (i))
+#define DP_CONFIG_RD_RESET_VAL_SHIFT 31
+#define DP_CONFIG_RD_RESET_VAL_POSITIVE (0 << 31)
+#define DP_CONFIG_RD_RESET_VAL_NEGATIVE (1 << 31)
+#define DP_CONFIG_IDLE_BEFORE_ATTACH_SHIFT 28
+#define DP_CONFIG_IDLE_BEFORE_ATTACH_DISABLE (0 << 28)
+#define DP_CONFIG_IDLE_BEFORE_ATTACH_ENABLE (1 << 28)
+#define DP_CONFIG_ACTIVESYM_CNTL_SHIFT 26
+#define DP_CONFIG_ACTIVESYM_CNTL_DISABLE (0 << 26)
+#define DP_CONFIG_ACTIVESYM_CNTL_ENABLE (1 << 26)
+#define DP_CONFIG_ACTIVESYM_POLARITY_SHIFT 24
+#define DP_CONFIG_ACTIVESYM_POLARITY_NEGATIVE (0 << 24)
+#define DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE (1 << 24)
+#define DP_CONFIG_ACTIVESYM_FRAC_SHIFT 16
+#define DP_CONFIG_ACTIVESYM_FRAC_MASK (0xf << 16)
+#define DP_CONFIG_ACTIVESYM_COUNT_SHIFT 8
+#define DP_CONFIG_ACTIVESYM_COUNT_MASK (0x7f << 8)
+#define DP_CONFIG_WATERMARK_SHIFT 0
+#define DP_CONFIG_WATERMARK_MASK 0x3f
+#define DP_MN(i) (0x5a + i)
+#define DP_MN_M_MOD_SHIFT 30
+#define DP_MN_M_MOD_DEFAULT_MASK (3 << 30)
+#define DP_MN_M_MOD_NONE (0 << 30)
+#define DP_MN_M_MOD_INC (1 << 30)
+#define DP_MN_M_MOD_DEC (2 << 30)
+#define DP_MN_M_DELTA_SHIFT 24
+#define DP_MN_M_DELTA_DEFAULT_MASK (0xf << 24)
+#define DP_MN_N_VAL_SHIFT 0
+#define DP_MN_N_VAL_DEFAULT_MASK 0xffffff
+#define DP_PADCTL(i) (0x5c + (i))
+#define DP_PADCTL_SPARE_SHIFT 25
+#define DP_PADCTL_SPARE_DEFAULT_MASK (0x7f << 25)
+#define DP_PADCTL_VCO_2X_SHIFT 24
+#define DP_PADCTL_VCO_2X_DISABLE (0 << 24)
+#define DP_PADCTL_VCO_2X_ENABLE (1 << 24)
+#define DP_PADCTL_PAD_CAL_PD_SHIFT 23
+#define DP_PADCTL_PAD_CAL_PD_POWERUP (0 << 23)
+#define DP_PADCTL_PAD_CAL_PD_POWERDOWN (1 << 23)
+#define DP_PADCTL_TX_PU_SHIFT 22
+#define DP_PADCTL_TX_PU_DISABLE (0 << 22)
+#define DP_PADCTL_TX_PU_ENABLE (1 << 22)
+#define DP_PADCTL_TX_PU_MASK (1 << 22)
+#define DP_PADCTL_REG_CTRL_SHIFT 20
+#define DP_PADCTL_REG_CTRL_DEFAULT_MASK (3 << 20)
+#define DP_PADCTL_VCMMODE_SHIFT 16
+#define DP_PADCTL_VCMMODE_DEFAULT_MASK (0xf << 16)
+#define DP_PADCTL_VCMMODE_TRISTATE (0 << 16)
+#define DP_PADCTL_VCMMODE_TEST_MUX (1 << 16)
+#define DP_PADCTL_VCMMODE_WEAK_PULLDOWN (2 << 16)
+#define DP_PADCTL_VCMMODE_STRONG_PULLDOWN (4 << 16)
+#define DP_PADCTL_TX_PU_VALUE_SHIFT 8
+#define DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK (0xff << 8)
+#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_SHIFT 7
+#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_DISABLE (0 << 7)
+#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_ENABLE (1 << 7)
+#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_SHIFT 6
+#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_DISABLE (0 << 6)
+#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_ENABLE (1 << 6)
+#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_SHIFT 5
+#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_DISABLE (0 << 5)
+#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_ENABLE (1 << 5)
+#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT 4
+#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_DISABLE (0 << 4)
+#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_ENABLE (1 << 4)
+#define DP_PADCTL_PD_TXD_3_SHIFT 3
+#define DP_PADCTL_PD_TXD_3_YES (0 << 3)
+#define DP_PADCTL_PD_TXD_3_NO (1 << 3)
+#define DP_PADCTL_PD_TXD_0_SHIFT 2
+#define DP_PADCTL_PD_TXD_0_YES (0 << 2)
+#define DP_PADCTL_PD_TXD_0_NO (1 << 2)
+#define DP_PADCTL_PD_TXD_1_SHIFT 1
+#define DP_PADCTL_PD_TXD_1_YES (0 << 1)
+#define DP_PADCTL_PD_TXD_1_NO (1 << 1)
+#define DP_PADCTL_PD_TXD_2_SHIFT 0
+#define DP_PADCTL_PD_TXD_2_YES 0
+#define DP_PADCTL_PD_TXD_2_NO 1
+#define DP_DEBUG(i) (0x5e + i)
+#define DP_SPARE(i) (0x60 + (i))
+#define DP_SPARE_REG_SHIFT 3
+#define DP_SPARE_REG_DEFAULT_MASK (0x1fffffff << 3)
+#define DP_SPARE_SOR_CLK_SEL_SHIFT 2
+#define DP_SPARE_SOR_CLK_SEL_DEFAULT_MASK (1 << 2)
+#define DP_SPARE_SOR_CLK_SEL_SAFE_SORCLK (0 << 2)
+#define DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK (1 << 2)
+#define DP_SPARE_PANEL_SHIFT 1
+#define DP_SPARE_PANEL_EXTERNAL (0 << 1)
+#define DP_SPARE_PANEL_INTERNAL (1 << 1)
+#define DP_SPARE_SEQ_ENABLE_SHIFT 0
+#define DP_SPARE_SEQ_ENABLE_NO 0
+#define DP_SPARE_SEQ_ENABLE_YES 1
+#define DP_AUDIO_CTRL 0x62
+#define DP_AUDIO_HBLANK_SYMBOLS 0x63
+#define DP_AUDIO_HBLANK_SYMBOLS_MASK 0x1ffff
+#define DP_AUDIO_HBLANK_SYMBOLS_VALUE_SHIFT 0
+#define DP_AUDIO_VBLANK_SYMBOLS 0x64
+#define DP_AUDIO_VBLANK_SYMBOLS_MASK 0x1ffff
+#define DP_AUDIO_VBLANK_SYMBOLS_SHIFT 0
+#define DP_GENERIC_INFOFRAME_HEADER 0x65
+#define DP_GENERIC_INFOFRAME_SUBPACK(i) (0x66 + (i))
+#define DP_TPG 0x6d
+#define DP_TPG_LANE3_CHANNELCODING_SHIFT 30
+#define DP_TPG_LANE3_CHANNELCODING_DISABLE (0 << 30)
+#define DP_TPG_LANE3_CHANNELCODING_ENABLE (1 << 30)
+#define DP_TPG_LANE3_SCRAMBLEREN_SHIFT 28
+#define DP_TPG_LANE3_SCRAMBLEREN_ENABLE_GALIOS (1 << 28)
+#define DP_TPG_LANE3_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 28)
+#define DP_TPG_LANE3_PATTERN_SHIFT 24
+#define DP_TPG_LANE3_PATTERN_DEFAULT_MASK (0xf << 24)
+#define DP_TPG_LANE3_PATTERN_NOPATTERN (0 << 24)
+#define DP_TPG_LANE3_PATTERN_TRAINING1 (1 << 24)
+#define DP_TPG_LANE3_PATTERN_TRAINING2 (2 << 24)
+#define DP_TPG_LANE3_PATTERN_TRAINING3 (3 << 24)
+#define DP_TPG_LANE3_PATTERN_D102 (4 << 24)
+#define DP_TPG_LANE3_PATTERN_SBLERRRATE (5 << 24)
+#define DP_TPG_LANE3_PATTERN_PRBS7 (6 << 24)
+#define DP_TPG_LANE3_PATTERN_CSTM (7 << 24)
+#define DP_TPG_LANE3_PATTERN_HBR2_COMPLIANCE (8 << 24)
+#define DP_TPG_LANE2_CHANNELCODING_SHIFT 22
+#define DP_TPG_LANE2_CHANNELCODING_DISABLE (0 << 22)
+#define DP_TPG_LANE2_CHANNELCODING_ENABLE (1 << 22)
+#define DP_TPG_LANE2_SCRAMBLEREN_SHIFT 20
+#define DP_TPG_LANE2_SCRAMBLEREN_DEFAULT_MASK (3 << 20)
+#define DP_TPG_LANE2_SCRAMBLEREN_DISABLE (0 << 20)
+#define DP_TPG_LANE2_SCRAMBLEREN_ENABLE_GALIOS (1 << 20)
+#define DP_TPG_LANE2_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 20)
+#define DP_TPG_LANE2_PATTERN_SHIFT 16
+#define DP_TPG_LANE2_PATTERN_DEFAULT_MASK (0xf << 16)
+#define DP_TPG_LANE2_PATTERN_NOPATTERN (0 << 16)
+#define DP_TPG_LANE2_PATTERN_TRAINING1 (1 << 16)
+#define DP_TPG_LANE2_PATTERN_TRAINING2 (2 << 16)
+#define DP_TPG_LANE2_PATTERN_TRAINING3 (3 << 16)
+#define DP_TPG_LANE2_PATTERN_D102 (4 << 16)
+#define DP_TPG_LANE2_PATTERN_SBLERRRATE (5 << 16)
+#define DP_TPG_LANE2_PATTERN_PRBS7 (6 << 16)
+#define DP_TPG_LANE2_PATTERN_CSTM (7 << 16)
+#define DP_TPG_LANE2_PATTERN_HBR2_COMPLIANCE (8 << 16)
+#define DP_TPG_LANE1_CHANNELCODING_SHIFT 14
+#define DP_TPG_LANE1_CHANNELCODING_DISABLE (0 << 14)
+#define DP_TPG_LANE1_CHANNELCODING_ENABLE (1 << 14)
+#define DP_TPG_LANE1_SCRAMBLEREN_SHIFT 12
+#define DP_TPG_LANE1_SCRAMBLEREN_DEFAULT_MASK (3 << 12)
+#define DP_TPG_LANE1_SCRAMBLEREN_DISABLE (0 << 12)
+#define DP_TPG_LANE1_SCRAMBLEREN_ENABLE_GALIOS (1 << 12)
+#define DP_TPG_LANE1_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 12)
+#define DP_TPG_LANE1_PATTERN_SHIFT 8
+#define DP_TPG_LANE1_PATTERN_DEFAULT_MASK (0xf << 8)
+#define DP_TPG_LANE1_PATTERN_NOPATTERN (0 << 8)
+#define DP_TPG_LANE1_PATTERN_TRAINING1 (1 << 8)
+#define DP_TPG_LANE1_PATTERN_TRAINING2 (2 << 8)
+#define DP_TPG_LANE1_PATTERN_TRAINING3 (3 << 8)
+#define DP_TPG_LANE1_PATTERN_D102 (4 << 8)
+#define DP_TPG_LANE1_PATTERN_SBLERRRATE (5 << 8)
+#define DP_TPG_LANE1_PATTERN_PRBS7 (6 << 8)
+#define DP_TPG_LANE1_PATTERN_CSTM (7 << 8)
+#define DP_TPG_LANE1_PATTERN_HBR2_COMPLIANCE (8 << 8)
+#define DP_TPG_LANE0_CHANNELCODING_SHIFT 6
+#define DP_TPG_LANE0_CHANNELCODING_DISABLE (0 << 6)
+#define DP_TPG_LANE0_CHANNELCODING_ENABLE (1 << 6)
+#define DP_TPG_LANE0_SCRAMBLEREN_SHIFT 4
+#define DP_TPG_LANE0_SCRAMBLEREN_DEFAULT_MASK (3 << 4)
+#define DP_TPG_LANE0_SCRAMBLEREN_DISABLE (0 << 4)
+#define DP_TPG_LANE0_SCRAMBLEREN_ENABLE_GALIOS (1 << 4)
+#define DP_TPG_LANE0_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 4)
+#define DP_TPG_LANE0_PATTERN_SHIFT 0
+#define DP_TPG_LANE0_PATTERN_DEFAULT_MASK 0xf
+#define DP_TPG_LANE0_PATTERN_NOPATTERN 0
+#define DP_TPG_LANE0_PATTERN_TRAINING1 1
+#define DP_TPG_LANE0_PATTERN_TRAINING2 2
+#define DP_TPG_LANE0_PATTERN_TRAINING3 3
+#define DP_TPG_LANE0_PATTERN_D102 4
+#define DP_TPG_LANE0_PATTERN_SBLERRRATE 5
+#define DP_TPG_LANE0_PATTERN_PRBS7 6
+#define DP_TPG_LANE0_PATTERN_CSTM 7
+#define DP_TPG_LANE0_PATTERN_HBR2_COMPLIANCE 8
+
+enum {
+ training_pattern_disabled = 0,
+ training_pattern_1 = 1,
+ training_pattern_2 = 2,
+ training_pattern_3 = 3,
+ training_pattern_none = 0xff
+};
+
+enum tegra_dc_sor_protocol {
+ SOR_DP,
+ SOR_LVDS,
+};
+
+#define SOR_LINK_SPEED_G1_62 6
+#define SOR_LINK_SPEED_G2_7 10
+#define SOR_LINK_SPEED_G5_4 20
+#define SOR_LINK_SPEED_LVDS 7
+
+struct tegra_dp_link_config {
+ int is_valid;
+
+ /* Supported configuration */
+ u8 max_link_bw;
+ u8 max_lane_count;
+ int downspread;
+ int support_enhanced_framing;
+ u32 bits_per_pixel;
+ int alt_scramber_reset_cap; /* true for eDP */
+ int only_enhanced_framing; /* enhanced_frame_en ignored */
+ int frame_in_ms;
+
+ /* Actual configuration */
+ u8 link_bw;
+ u8 lane_count;
+ int enhanced_framing;
+ int scramble_ena;
+
+ u32 activepolarity;
+ u32 active_count;
+ u32 tu_size;
+ u32 active_frac;
+ u32 watermark;
+
+ s32 hblank_sym;
+ s32 vblank_sym;
+
+ /* Training data */
+ u32 drive_current;
+ u32 preemphasis;
+ u32 postcursor;
+ u8 aux_rd_interval;
+ u8 tps3_supported;
+};
+
+struct tegra_dc_sor_data {
+ void *base;
+ void *pmc_base;
+ u8 portnum; /* 0 or 1 */
+ int power_is_up;
+};
+
+#define TEGRA_SOR_TIMEOUT_MS 1000
+#define TEGRA_SOR_ATTACH_TIMEOUT_MS 1000
+
+int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd);
+void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+ u8 training_pattern, const struct tegra_dp_link_config *link_cfg);
+void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw);
+void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count);
+void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
+ int power_up);
+void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int);
+void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+ u8 *lane_count);
+void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *cfg);
+void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor);
+void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask,
+ u32 pe_reg, u32 vs_reg, u32 pc_reg, u8 pc_supported);
+
+int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing);
+int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor);
+
+void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx);
+int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl);
+void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx);
+
+int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp);
+#endif
diff --git a/drivers/video/tegra124/tegra124-lcd.c b/drivers/video/tegra124/tegra124-lcd.c
new file mode 100644
index 0000000..2733590
--- /dev/null
+++ b/drivers/video/tegra124/tegra124-lcd.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <lcd.h>
+#include <asm/gpio.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/arch-tegra/dc.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum {
+ /* Maximum LCD size we support */
+ LCD_MAX_WIDTH = 1920,
+ LCD_MAX_HEIGHT = 1200,
+ LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
+};
+
+vidinfo_t panel_info = {
+ /* Insert a value here so that we don't end up in the BSS */
+ .vl_col = -1,
+};
+
+int tegra_lcd_check_next_stage(const void *blob, int wait)
+{
+ return 0;
+}
+
+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;
+}
+
+static int tegra124_lcd_init(void *lcdbase)
+{
+ struct display_timing timing;
+ int ret;
+
+ clock_set_up_plldp();
+ clock_adjust_periph_pll_div(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
+ 408000000, NULL);
+
+ clock_enable(PERIPH_ID_HOST1X);
+ clock_enable(PERIPH_ID_DISP1);
+ clock_enable(PERIPH_ID_PWM);
+ clock_enable(PERIPH_ID_DPAUX);
+ clock_enable(PERIPH_ID_SOR0);
+
+ udelay(2);
+
+ reset_set_enable(PERIPH_ID_HOST1X, 0);
+ reset_set_enable(PERIPH_ID_DISP1, 0);
+ reset_set_enable(PERIPH_ID_PWM, 0);
+ reset_set_enable(PERIPH_ID_DPAUX, 0);
+ reset_set_enable(PERIPH_ID_SOR0, 0);
+
+ ret = display_init(lcdbase, 1 << LCD_BPP, &timing);
+ if (ret)
+ return ret;
+
+ panel_info.vl_col = roundup(timing.hactive.typ, 16);
+ panel_info.vl_row = timing.vactive.typ;
+
+ lcd_set_flush_dcache(1);
+
+ return 0;
+}
+
+void lcd_ctrl_init(void *lcdbase)
+{
+ ulong start;
+ int ret;
+
+ start = get_timer(0);
+ ret = tegra124_lcd_init(lcdbase);
+ debug("LCD init took %lu ms\n", get_timer(start));
+ if (ret)
+ printf("%s: Error %d\n", __func__, ret);
+}
+
+void lcd_enable(void)
+{
+}