]> git.karo-electronics.de Git - karo-tx-redboot.git/blobdiff - packages/hal/arm/mx27/karo/v1_0/src/tx27_misc.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / hal / arm / mx27 / karo / v1_0 / src / tx27_misc.c
index 474f109f6bfa470e02520e3f1c44dce33732fd46..6446d77d9dcd1792923e76e178f0e662ed8e7e17 100644 (file)
@@ -39,6 +39,7 @@
 //####ECOSGPLCOPYRIGHTEND####
 //========================================================================*/
 
+#include <stdlib.h>
 #include <redboot.h>
 #include <string.h>
 #include <pkgconf/hal.h>
@@ -83,7 +84,7 @@ void hal_mmu_init(void)
         */
        memset((void *)ttb_base, 0, ARM_FIRST_LEVEL_PAGE_TABLE_SIZE);
 
-       /*             Actual    Virtual Size   Attributes                                                    Function   */
+       /*             Physical  Virtual Size   Attributes                                                    Function   */
        /*             Base      Base     MB     cached?           buffered?        access permissions                  */
        /*             xxx00000  xxx00000                                                                                */
        X_ARM_MMU_SECTION(0x000, 0xF00, 0x001, ARM_CACHEABLE,   ARM_BUFFERABLE,   ARM_ACCESS_PERM_RW_RW); /* Boot Rom */
@@ -176,15 +177,306 @@ void plf_hardware_init(void)
        fec_gpio_init();
 }
 
-static void tx27_raise_voltage(void)
+#define SOC_FBAC0_REG          0x10028800UL
+
+extern int fuse_blow(int bank, int row, int bit);
+
+#define SOC_I2C2_BASE          UL(0x1001D000)
+
+/* Address offsets of the I2C registers */
+#define MXC_IADR                0x00   /* Address Register */
+#define MXC_IFDR                0x04   /* Freq div register */
+#define MXC_I2CR                0x08   /* Control regsiter */
+#define MXC_I2SR                0x0C   /* Status register */
+#define MXC_I2DR                0x10   /* Data I/O register */
+
+/* Bit definitions of I2CR */
+#define MXC_I2CR_IEN            0x0080
+#define MXC_I2CR_IIEN           0x0040
+#define MXC_I2CR_MSTA           0x0020
+#define MXC_I2CR_MTX            0x0010
+#define MXC_I2CR_TXAK           0x0008
+#define MXC_I2CR_RSTA           0x0004
+
+/* Bit definitions of I2SR */
+#define MXC_I2SR_ICF            0x0080
+#define MXC_I2SR_IAAS           0x0040
+#define MXC_I2SR_IBB            0x0020
+#define MXC_I2SR_IAL            0x0010
+#define MXC_I2SR_SRW            0x0004
+#define MXC_I2SR_IIF            0x0002
+#define MXC_I2SR_RXAK           0x0001
+
+#define LP3972_SLAVE_ADDR      0x34
+
+static inline cyg_uint8 i2c_addr(cyg_uint8 addr, int rw)
+{
+       return (addr << 1) | !!rw;
+}
+
+static inline cyg_uint8 mx27_i2c_read(cyg_uint8 reg)
+{
+       cyg_uint16 val;
+       HAL_READ_UINT16(SOC_I2C2_BASE + reg, val);
+       return val;
+}
+
+static inline void mx27_i2c_write(cyg_uint8 reg, cyg_uint8 val)
+{
+       HAL_WRITE_UINT16(SOC_I2C2_BASE + reg, val);
+}
+
+static inline void mx27_i2c_set_reg(cyg_uint8 reg, cyg_uint8 set, cyg_uint8 clr)
+{
+       cyg_uint8 val = mx27_i2c_read(reg);
+       val = (val & ~clr) | set;
+       mx27_i2c_write(reg, val);
+}
+
+static void mx27_i2c_disable(void)
+{
+       /* disable I2C controller */
+       mx27_i2c_set_reg(MXC_I2CR, 0, MXC_I2CR_IEN);
+       /* disable I2C clock */
+       set_reg(SOC_CRM_PCCR0, 0, (1 << 17));
+}
+
+static int mx27_i2c_init(void)
+{
+       int ret;
+
+       /* configure PC5,PC6 as Primary Function */
+       set_reg(SOC_GPIOC_BASE + GPIO_GPR, 0, GPR_MASK(5) | GPR_MASK(6));
+       set_reg(SOC_GPIOC_BASE + GPIO_GIUS, 0, GPR_MASK(5) | GPR_MASK(6));
+
+       /* enable I2C clock */
+       set_reg(SOC_CRM_PCCR0, (1 << 17), 0);
+
+       /* setup I2C clock divider */
+       mx27_i2c_write(MXC_IFDR, 0x2c);
+       mx27_i2c_write(MXC_I2SR, 0);
+
+       /* enable I2C controller in master mode */
+       mx27_i2c_write(MXC_I2CR, MXC_I2CR_IEN);
+
+       ret = mx27_i2c_read(MXC_I2SR);
+       if (ret & MXC_I2SR_IBB) {
+               diag_printf("I2C bus busy\n");
+               mx27_i2c_disable();
+               return -EIO;
+       }
+       return 0;
+}
+
+static int mx27_i2c_wait_busy(int set)
+{
+       int ret;
+       const int max_loops = 100;
+       int retries = max_loops;
+
+       cyg_uint8 mask = set ? MXC_I2SR_IBB : 0;
+
+       while ((ret = mask ^ (mx27_i2c_read(MXC_I2SR) & MXC_I2SR_IBB)) && --retries > 0) {
+               HAL_DELAY_US(3);
+       }
+       if (ret != 0) {
+               diag_printf("i2c: Waiting for IBB to %s timed out\n", set ? "set" : "clear");
+               return -ETIMEDOUT;
+       }
+       return ret;
+}
+
+static int mx27_i2c_wait_tc(void)
+{
+       int ret;
+       const int max_loops = 1000;
+       int retries = max_loops;
+
+       while (!((ret = mx27_i2c_read(MXC_I2SR)) & MXC_I2SR_IIF) && --retries > 0) {
+               HAL_DELAY_US(3);
+       }
+       mx27_i2c_write(MXC_I2SR, 0);
+       if (!(ret & MXC_I2SR_IIF)) {
+               diag_printf("i2c: Wait for transfer completion timed out\n");
+               return -ETIMEDOUT;
+       }
+       if (ret & MXC_I2SR_ICF) {
+               if (mx27_i2c_read(MXC_I2CR) & MXC_I2CR_MTX) {
+                       if (!(ret & MXC_I2SR_RXAK)) {
+                               ret = 0;
+                       } else {
+                               diag_printf("i2c: No ACK received after writing data\n");
+                               return -ENXIO;
+                       }
+               }
+       }
+       return ret;
+}
+
+static int mx27_i2c_stop(void)
+{
+       int ret;
+
+       mx27_i2c_set_reg(MXC_I2CR, 0, MXC_I2CR_MSTA | MXC_I2CR_MTX);
+       ret = mx27_i2c_wait_busy(0);
+       return ret;
+}
+
+static int mx27_i2c_start(cyg_uint8 addr, int rw)
+{
+       int ret;
+
+       ret = mx27_i2c_init();
+       if (ret < 0) {
+               diag_printf("I2C bus init failed; cannot switch fuse programming voltage\n");
+               return ret;
+       }
+       mx27_i2c_set_reg(MXC_I2CR, MXC_I2CR_MSTA, 0);
+       ret = mx27_i2c_wait_busy(1);
+       if (ret == 0) {
+               mx27_i2c_set_reg(MXC_I2CR, MXC_I2CR_MTX, 0);
+               mx27_i2c_write(MXC_I2DR, i2c_addr(addr, rw));
+               ret = mx27_i2c_wait_tc();
+               if (ret < 0) {
+                       mx27_i2c_stop();
+               }
+       }
+       return ret;
+}
+
+static int mx27_i2c_repeat_start(cyg_uint8 addr, int rw)
+{
+       int ret;
+
+       mx27_i2c_set_reg(MXC_I2CR, MXC_I2CR_RSTA, 0);
+       HAL_DELAY_US(3);
+       mx27_i2c_write(MXC_I2DR, i2c_addr(addr, rw));
+       ret = mx27_i2c_wait_tc();
+       if (ret < 0) {
+               mx27_i2c_stop();
+       }
+       return ret;
+}
+
+static int mx27_i2c_read_byte(void)
+{
+       int ret;
+
+       mx27_i2c_set_reg(MXC_I2CR, MXC_I2CR_TXAK, MXC_I2CR_MTX);
+       (void)mx27_i2c_read(MXC_I2DR); /* dummy read after address cycle */
+       ret = mx27_i2c_wait_tc();
+       mx27_i2c_stop();
+       if (ret < 0) {
+               return ret;
+       }
+       ret = mx27_i2c_read(MXC_I2DR);
+       return ret;
+}
+
+static int mx27_i2c_write_byte(cyg_uint8 data, int last)
+{
+       int ret;
+
+       mx27_i2c_set_reg(MXC_I2CR, MXC_I2CR_MTX, 0);
+       mx27_i2c_write(MXC_I2DR, data);
+       if ((ret = mx27_i2c_wait_tc()) < 0 || last) {
+               mx27_i2c_stop();
+       }
+       return ret;
+}
+
+static int lp3972_reg_read(cyg_uint8 reg)
+{
+       int ret;
+
+       ret = mx27_i2c_start(LP3972_SLAVE_ADDR, 0);
+       if (ret < 0) {
+               return ret;
+       }
+       ret = mx27_i2c_write_byte(reg, 0);
+       if (ret < 0) {
+               return ret;
+       }
+       ret = mx27_i2c_repeat_start(LP3972_SLAVE_ADDR, 1);
+       if (ret < 0) {
+               return ret;
+       }
+       ret = mx27_i2c_read_byte();
+       mx27_i2c_disable();
+       return ret;
+}
+
+static int lp3972_reg_write(cyg_uint8 reg, cyg_uint8 val)
 {
-#if defined (CLOCK_399_133_66)
-       /* Increase core voltage to 1.45 */
-       setCoreVoltage(0x16);
-#endif
+       int ret;
+
+       ret = mx27_i2c_start(LP3972_SLAVE_ADDR, 0);
+       if (ret < 0) {
+               return ret;
+       }
+       ret = mx27_i2c_write_byte(reg, 0);
+       if (ret < 0) {
+               return ret;
+       }
+       ret = mx27_i2c_write_byte(val, 1);
+       mx27_i2c_disable();
+       return ret;
 }
 
-RedBoot_init(tx27_raise_voltage, RedBoot_INIT_PRIO(101));
+int tx27_mac_addr_program(unsigned char mac_addr[ETHER_ADDR_LEN])
+{
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < ETHER_ADDR_LEN; i++) {
+               unsigned char fuse = readl(SOC_FEC_MAC_BASE2 + (i << 2));
+
+               if ((fuse | mac_addr[i]) != mac_addr[i]) {
+                       diag_printf("MAC address fuse cannot be programmed: fuse[%d]=0x%02x -> 0x%02x\n",
+                                   i, fuse, mac_addr[i]);
+                       return -1;
+               }
+               if (fuse != mac_addr[i]) {
+                       ret = 1;
+               }
+       }
+       if (ret == 0) {
+               return ret;
+       }
+       ret = lp3972_reg_write(0x39, 0xf0);
+       if (ret < 0) {
+               diag_printf("Failed to switch fuse programming voltage\n");
+               return ret;
+       }
+       ret = lp3972_reg_read(0x39);
+       if (ret != 0xf0) {
+               diag_printf("Failed to switch fuse programming voltage\n");
+               return ret;
+       }
+       for (i = 0; i < ETHER_ADDR_LEN; i++) {
+               int bit;
+               unsigned char fuse = readl(SOC_FEC_MAC_BASE2 + (i << 2));
+
+               for (bit = 0; bit < 8; bit++) {
+                       if (((mac_addr[i] >> bit) & 0x1) == 0)
+                               continue;
+                       if (((mac_addr[i] >> bit) & 1) == ((fuse >> bit) & 1)) {
+                               continue;
+                       }
+                       if (fuse_blow(0, i + 5, bit)) {
+                               diag_printf("Failed to blow fuse bank 0 row %d bit %d\n",
+                                           i, bit);
+                               ret = -1;
+                               goto out;
+                       }
+               }
+       }
+       /* would like to blow the MAC_ADDR_LOCK fuse, but that's not available on MX27 */
+       //fuse_blow(0, 0, SOC_MAC_ADDR_LOCK_BIT);
+out:
+       lp3972_reg_write(0x39, 0);
+       return ret;
+}
 
 #include CYGHWR_MEMORY_LAYOUT_H
 
@@ -215,9 +507,43 @@ static void display_clock_src(void)
        }
 }
 
+static unsigned long random;
+extern unsigned int hal_timer_count(void);
+/* provide at least _some_ sort of randomness */
+static void random_init(void)
+{
+       do {
+               srand(random + hal_timer_count());
+               random = rand();
+       } while ((hal_timer_count() < 5) || (hal_timer_count() & 0x47110815));
+}
+RedBoot_init(random_init, RedBoot_INIT_FIRST);
+
+#define WDOG_WRSR      ((CYG_WORD16 *)0x10002004)
 static void display_board_type(void)
 {
+       char *reset_cause;
+       CYG_WORD16 wrsr;
+
        diag_printf("\nBoard Type: Ka-Ro TX27\n");
+       HAL_READ_UINT16(WDOG_WRSR, wrsr);
+       switch (wrsr) {
+       case (1 << 4):
+               reset_cause = "POWER_ON RESET";
+               break;
+       case (1 << 3):
+               reset_cause = "EXTERNAL RESET";
+               break;
+       case (1 << 1):
+               reset_cause = "WATCHDOG RESET";
+               break;
+       case (1 << 0):
+               reset_cause = "SOFT RESET";
+               break;
+       default:
+               reset_cause = "UNKNOWN";
+       }
+       diag_printf("Last RESET cause: %s\n", reset_cause);
 }
 
 static void display_board_info(void)
@@ -227,4 +553,3 @@ static void display_board_info(void)
 }
 
 RedBoot_init(display_board_info, RedBoot_INIT_LAST);
-// ------------------------------------------------------------------------