mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-14 00:47:26 +00:00
b87ca9907c
The PMIC on the Colibri iMX6 may have ECC errors in fuses that will prevent correct settings. Up to one bit error per fuse bank can be reported and corrected by the ECC logic. Two bit errors can only be reported. Signed-off-by: Gerard Salvatella <gerard.salvatella@toradex.com> Acked-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
267 lines
6.1 KiB
C
267 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2014-2019, Toradex AG
|
|
*/
|
|
|
|
/*
|
|
* Helpers for Freescale PMIC PF0100
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <i2c.h>
|
|
#include <asm/arch/imx-regs.h>
|
|
#include <asm/arch/iomux.h>
|
|
#include <asm/arch/mx6-pins.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/mach-imx/iomux-v3.h>
|
|
|
|
#include "pf0100_otp.inc"
|
|
#include "pf0100.h"
|
|
|
|
/* define for PMIC register dump */
|
|
/*#define DEBUG */
|
|
|
|
#define WARNBAR "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
|
|
|
|
/* use GPIO: EXT_IO1 to switch on VPGM, ON: 1 */
|
|
static __maybe_unused iomux_v3_cfg_t const pmic_prog_pads[] = {
|
|
MX6_PAD_NANDF_D3__GPIO2_IO03 | MUX_PAD_CTRL(NO_PAD_CTRL),
|
|
# define PMIC_PROG_VOLTAGE IMX_GPIO_NR(2, 3)
|
|
};
|
|
|
|
unsigned pmic_init(void)
|
|
{
|
|
int rc;
|
|
struct udevice *dev = NULL;
|
|
unsigned programmed = 0;
|
|
uchar bus = 1;
|
|
uchar devid, revid, val;
|
|
|
|
puts("PMIC: ");
|
|
rc = i2c_get_chip_for_busnum(bus, PFUZE100_I2C_ADDR, 1, &dev);
|
|
if (rc) {
|
|
printf("failed to get device for PMIC at address 0x%x\n",
|
|
PFUZE100_I2C_ADDR);
|
|
return 0;
|
|
}
|
|
|
|
/* check for errors in PMIC fuses */
|
|
if (dm_i2c_read(dev, PFUZE100_INTSTAT3, &val, 1) < 0) {
|
|
puts("i2c pmic INTSTAT3 register read failed\n");
|
|
return 0;
|
|
}
|
|
if (val & PFUZE100_BIT_OTP_ECCI) {
|
|
puts("\n" WARNBAR);
|
|
puts("WARNING: ecc errors found in pmic fuse banks\n");
|
|
puts(WARNBAR);
|
|
}
|
|
if (dm_i2c_read(dev, PFUZE100_OTP_ECC_SE1, &val, 1) < 0) {
|
|
puts("i2c pmic ECC_SE1 register read failed\n");
|
|
return 0;
|
|
}
|
|
if (val & PFUZE100_BITS_ECC_SE1) {
|
|
puts(WARNBAR);
|
|
puts("WARNING: ecc has made bit corrections in banks 1 to 5\n");
|
|
puts(WARNBAR);
|
|
}
|
|
if (dm_i2c_read(dev, PFUZE100_OTP_ECC_SE2, &val, 1) < 0) {
|
|
puts("i2c pmic ECC_SE2 register read failed\n");
|
|
return 0;
|
|
}
|
|
if (val & PFUZE100_BITS_ECC_SE2) {
|
|
puts(WARNBAR);
|
|
puts("WARNING: ecc has made bit corrections in banks 6 to 10\n"
|
|
);
|
|
puts(WARNBAR);
|
|
}
|
|
if (dm_i2c_read(dev, PFUZE100_OTP_ECC_DE1, &val, 1) < 0) {
|
|
puts("i2c pmic ECC_DE register read failed\n");
|
|
return 0;
|
|
}
|
|
if (val & PFUZE100_BITS_ECC_DE1) {
|
|
puts(WARNBAR);
|
|
puts("ERROR: banks 1 to 5 have uncorrectable bits\n");
|
|
puts(WARNBAR);
|
|
}
|
|
if (dm_i2c_read(dev, PFUZE100_OTP_ECC_DE2, &val, 1) < 0) {
|
|
puts("i2c pmic ECC_DE register read failed\n");
|
|
return 0;
|
|
}
|
|
if (val & PFUZE100_BITS_ECC_DE2) {
|
|
puts(WARNBAR);
|
|
puts("ERROR: banks 6 to 10 have uncorrectable bits\n");
|
|
puts(WARNBAR);
|
|
}
|
|
|
|
/* get device ident */
|
|
if (dm_i2c_read(dev, PFUZE100_DEVICEID, &devid, 1) < 0) {
|
|
puts("i2c pmic devid read failed\n");
|
|
return 0;
|
|
}
|
|
if (dm_i2c_read(dev, PFUZE100_REVID, &revid, 1) < 0) {
|
|
puts("i2c pmic revid read failed\n");
|
|
return 0;
|
|
}
|
|
printf("device id: 0x%.2x, revision id: 0x%.2x, ", devid, revid);
|
|
|
|
/* get device programmed state */
|
|
val = PFUZE100_PAGE_REGISTER_PAGE1;
|
|
if (dm_i2c_write(dev, PFUZE100_PAGE_REGISTER, &val, 1)) {
|
|
puts("i2c write failed\n");
|
|
return 0;
|
|
}
|
|
if (dm_i2c_read(dev, PFUZE100_FUSE_POR1, &val, 1) < 0) {
|
|
puts("i2c fuse_por read failed\n");
|
|
return 0;
|
|
}
|
|
if (val & PFUZE100_FUSE_POR_M)
|
|
programmed++;
|
|
|
|
if (dm_i2c_read(dev, PFUZE100_FUSE_POR2, &val, 1) < 0) {
|
|
puts("i2c fuse_por read failed\n");
|
|
return programmed;
|
|
}
|
|
if (val & PFUZE100_FUSE_POR_M)
|
|
programmed++;
|
|
|
|
if (dm_i2c_read(dev, PFUZE100_FUSE_POR3, &val, 1) < 0) {
|
|
puts("i2c fuse_por read failed\n");
|
|
return programmed;
|
|
}
|
|
if (val & PFUZE100_FUSE_POR_M)
|
|
programmed++;
|
|
|
|
switch (programmed) {
|
|
case 0:
|
|
puts("not programmed\n");
|
|
break;
|
|
case 3:
|
|
puts("programmed\n");
|
|
break;
|
|
default:
|
|
puts("undefined programming state\n");
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
unsigned int i, j;
|
|
|
|
for (i = 0; i < 16; i++)
|
|
printf("\t%x", i);
|
|
for (j = 0; j < 0x80; ) {
|
|
printf("\n%2x", j);
|
|
for (i = 0; i < 16; i++) {
|
|
dm_i2c_read(dev, j + i, &val, 1);
|
|
printf("\t%2x", val);
|
|
}
|
|
j += 0x10;
|
|
}
|
|
printf("\nEXT Page 1");
|
|
|
|
val = PFUZE100_PAGE_REGISTER_PAGE1;
|
|
if (dm_i2c_write(dev, PFUZE100_PAGE_REGISTER, &val, 1)) {
|
|
puts("i2c write failed\n");
|
|
return 0;
|
|
}
|
|
|
|
for (j = 0x80; j < 0x100; ) {
|
|
printf("\n%2x", j);
|
|
for (i = 0; i < 16; i++) {
|
|
dm_i2c_read(dev, j + i, &val, 1);
|
|
printf("\t%2x", val);
|
|
}
|
|
j += 0x10;
|
|
}
|
|
printf("\nEXT Page 2");
|
|
|
|
val = PFUZE100_PAGE_REGISTER_PAGE2;
|
|
if (dm_i2c_write(dev, PFUZE100_PAGE_REGISTER, &val, 1)) {
|
|
puts("i2c write failed\n");
|
|
return 0;
|
|
}
|
|
|
|
for (j = 0x80; j < 0x100; ) {
|
|
printf("\n%2x", j);
|
|
for (i = 0; i < 16; i++) {
|
|
dm_i2c_read(dev, j + i, &val, 1);
|
|
printf("\t%2x", val);
|
|
}
|
|
j += 0x10;
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
return programmed;
|
|
}
|
|
|
|
#ifndef CONFIG_SPL_BUILD
|
|
static int pf0100_prog(void)
|
|
{
|
|
int rc;
|
|
struct udevice *dev = NULL;
|
|
unsigned char bus = 1;
|
|
unsigned char val;
|
|
unsigned int i;
|
|
|
|
if (pmic_init() == 3) {
|
|
puts("PMIC already programmed, exiting\n");
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
/* set up gpio to manipulate vprog, initially off */
|
|
imx_iomux_v3_setup_multiple_pads(pmic_prog_pads,
|
|
ARRAY_SIZE(pmic_prog_pads));
|
|
gpio_direction_output(PMIC_PROG_VOLTAGE, 0);
|
|
|
|
rc = i2c_get_chip_for_busnum(bus, PFUZE100_I2C_ADDR, 1, &dev);
|
|
if (rc) {
|
|
printf("failed to get device for PMIC at address 0x%x\n",
|
|
PFUZE100_I2C_ADDR);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pmic_otp_prog); i++) {
|
|
switch (pmic_otp_prog[i].cmd) {
|
|
case pmic_i2c:
|
|
val = (unsigned char) (pmic_otp_prog[i].value & 0xff);
|
|
if (dm_i2c_write(dev, pmic_otp_prog[i].reg, &val, 1)) {
|
|
printf("i2c write failed, reg 0x%2x, value 0x%2x\n",
|
|
pmic_otp_prog[i].reg, val);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
break;
|
|
case pmic_delay:
|
|
udelay(pmic_otp_prog[i].value * 1000);
|
|
break;
|
|
case pmic_vpgm:
|
|
gpio_direction_output(PMIC_PROG_VOLTAGE,
|
|
pmic_otp_prog[i].value);
|
|
break;
|
|
case pmic_pwr:
|
|
/* TODO */
|
|
break;
|
|
}
|
|
}
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_pf0100_prog(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
int ret;
|
|
puts("Programming PMIC OTP...");
|
|
ret = pf0100_prog();
|
|
if (ret == CMD_RET_SUCCESS)
|
|
puts("done.\n");
|
|
else
|
|
puts("failed.\n");
|
|
return ret;
|
|
}
|
|
|
|
U_BOOT_CMD(
|
|
pf0100_otp_prog, 1, 0, do_pf0100_prog,
|
|
"Program the OTP fuses on the PMIC PF0100",
|
|
""
|
|
);
|
|
#endif /* CONFIG_SPL_BUILD */
|