/* 
 | 
 * Copyright (C) 2016 Stefan Roese <sr@denx.de> 
 | 
 * 
 | 
 * SPDX-License-Identifier:    GPL-2.0+ 
 | 
 */ 
 | 
  
 | 
#include <common.h> 
 | 
#include <dm.h> 
 | 
#include <i2c.h> 
 | 
#include <asm/io.h> 
 | 
#include <asm/arch/cpu.h> 
 | 
#include <asm/arch/soc.h> 
 | 
  
 | 
DECLARE_GLOBAL_DATA_PTR; 
 | 
  
 | 
/* 
 | 
 * Information specific to the DB-88F7040 eval board. We strive to use 
 | 
 * DT for such platform specfic configurations. At some point, this 
 | 
 * might be removed here and implemented via DT. 
 | 
 */ 
 | 
/* IO expander I2C device */ 
 | 
#define I2C_IO_EXP_ADDR        0x21 
 | 
#define I2C_IO_CFG_REG_0    0x6 
 | 
#define I2C_IO_DATA_OUT_REG_0    0x2 
 | 
/* VBus enable */ 
 | 
#define I2C_IO_REG_0_USB_H0_OFF    0 
 | 
#define I2C_IO_REG_0_USB_H1_OFF    1 
 | 
#define I2C_IO_REG_VBUS        ((1 << I2C_IO_REG_0_USB_H0_OFF) | \ 
 | 
                 (1 << I2C_IO_REG_0_USB_H1_OFF)) 
 | 
/* Current limit */ 
 | 
#define I2C_IO_REG_0_USB_H0_CL    4 
 | 
#define I2C_IO_REG_0_USB_H1_CL    5 
 | 
#define I2C_IO_REG_CL        ((1 << I2C_IO_REG_0_USB_H0_CL) | \ 
 | 
                 (1 << I2C_IO_REG_0_USB_H1_CL)) 
 | 
  
 | 
static int usb_enabled = 0; 
 | 
  
 | 
/* Board specific xHCI dis-/enable code */ 
 | 
  
 | 
/* 
 | 
 * Set USB VBUS signals (via I2C IO expander/GPIO) as output and set 
 | 
 * output value as disabled 
 | 
 * 
 | 
 * Set USB Current Limit signals (via I2C IO expander/GPIO) as output 
 | 
 * and set output value as enabled 
 | 
 */ 
 | 
int board_xhci_config(void) 
 | 
{ 
 | 
    struct udevice *dev; 
 | 
    int ret; 
 | 
    u8 buf[8]; 
 | 
  
 | 
    if (of_machine_is_compatible("marvell,armada7040-db")) { 
 | 
        /* Configure IO exander PCA9555: 7bit address 0x21 */ 
 | 
        ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev); 
 | 
        if (ret) { 
 | 
            printf("Cannot find PCA9555: %d\n", ret); 
 | 
            return 0; 
 | 
        } 
 | 
  
 | 
        /* 
 | 
         * Read configuration (direction) and set VBUS pin as output 
 | 
         * (reset pin = output) 
 | 
         */ 
 | 
        ret = dm_i2c_read(dev, I2C_IO_CFG_REG_0, buf, 1); 
 | 
        if (ret) { 
 | 
            printf("Failed to read IO expander value via I2C\n"); 
 | 
            return -EIO; 
 | 
        } 
 | 
        buf[0] &= ~I2C_IO_REG_VBUS; 
 | 
        buf[0] &= ~I2C_IO_REG_CL; 
 | 
        ret = dm_i2c_write(dev, I2C_IO_CFG_REG_0, buf, 1); 
 | 
        if (ret) { 
 | 
            printf("Failed to set IO expander via I2C\n"); 
 | 
            return -EIO; 
 | 
        } 
 | 
  
 | 
        /* Read output value and configure it */ 
 | 
        ret = dm_i2c_read(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 
 | 
        if (ret) { 
 | 
            printf("Failed to read IO expander value via I2C\n"); 
 | 
            return -EIO; 
 | 
        } 
 | 
        buf[0] &= ~I2C_IO_REG_VBUS; 
 | 
        buf[0] |= I2C_IO_REG_CL; 
 | 
        ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 
 | 
        if (ret) { 
 | 
            printf("Failed to set IO expander via I2C\n"); 
 | 
            return -EIO; 
 | 
        } 
 | 
  
 | 
        mdelay(500); /* required delay to let output value settle */ 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int board_xhci_enable(fdt_addr_t base) 
 | 
{ 
 | 
    struct udevice *dev; 
 | 
    int ret; 
 | 
    u8 buf[8]; 
 | 
  
 | 
    if (of_machine_is_compatible("marvell,armada7040-db")) { 
 | 
        /* 
 | 
         * This function enables all USB ports simultaniously, 
 | 
         * it only needs to get called once 
 | 
         */ 
 | 
        if (usb_enabled) 
 | 
            return 0; 
 | 
  
 | 
        /* Configure IO exander PCA9555: 7bit address 0x21 */ 
 | 
        ret = i2c_get_chip_for_busnum(0, I2C_IO_EXP_ADDR, 1, &dev); 
 | 
        if (ret) { 
 | 
            printf("Cannot find PCA9555: %d\n", ret); 
 | 
            return 0; 
 | 
        } 
 | 
  
 | 
        /* Read VBUS output value */ 
 | 
        ret = dm_i2c_read(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 
 | 
        if (ret) { 
 | 
            printf("Failed to read IO expander value via I2C\n"); 
 | 
            return -EIO; 
 | 
        } 
 | 
  
 | 
        /* Enable VBUS power: Set output value of VBUS pin as enabled */ 
 | 
        buf[0] |= I2C_IO_REG_VBUS; 
 | 
        ret = dm_i2c_write(dev, I2C_IO_DATA_OUT_REG_0, buf, 1); 
 | 
        if (ret) { 
 | 
            printf("Failed to set IO expander via I2C\n"); 
 | 
            return -EIO; 
 | 
        } 
 | 
  
 | 
        mdelay(500); /* required delay to let output value settle */ 
 | 
        usb_enabled = 1; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int board_early_init_f(void) 
 | 
{ 
 | 
    /* Nothing to do (yet), perhaps later some pin-muxing etc */ 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int board_init(void) 
 | 
{ 
 | 
    /* adress of boot parameters */ 
 | 
    gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int board_late_init(void) 
 | 
{ 
 | 
    /* Pre-configure the USB ports (overcurrent, VBus) */ 
 | 
    board_xhci_config(); 
 | 
  
 | 
    return 0; 
 | 
} 
 |