Commit 0f67e09e authored by Benoît Thébaudeau's avatar Benoît Thébaudeau Committed by Stefano Babic

Add fsl_iim driver

Add a fsl_iim driver common to i.MX and MPC.
Signed-off-by: 's avatarBenoît Thébaudeau <benoit.thebaudeau@advansee.com>
parent ccca7dfd
......@@ -253,6 +253,7 @@ struct aips_regs {
#define IMX_PWM1_BASE (0x53FE0000)
#define IMX_RTIC_BASE (0x53FEC000)
#define IMX_IIM_BASE (0x53FF0000)
#define IIM_BASE_ADDR IMX_IIM_BASE
#define IMX_USB_BASE (0x53FF4000)
#define IMX_USB_PORT_OFFSET 0x200
#define IMX_CSI_BASE (0x53FF8000)
......
......@@ -222,6 +222,7 @@ struct fuse_bank0_regs {
#define IMX_PLL_BASE (0x27000 + IMX_IO_BASE)
#define IMX_SYSTEM_CTL_BASE (0x27800 + IMX_IO_BASE)
#define IMX_IIM_BASE (0x28000 + IMX_IO_BASE)
#define IIM_BASE_ADDR IMX_IIM_BASE
#define IMX_FEC_BASE (0x2b000 + IMX_IO_BASE)
#define IMX_ESD_BASE (0xD8001000)
......
......@@ -574,6 +574,7 @@ struct esdc_regs {
#define CCMR_CKIH (2 << 1)
#define MX31_IIM_BASE_ADDR 0x5001C000
#define IIM_BASE_ADDR MX31_IIM_BASE_ADDR
#define PDR0_CSI_PODF(x) (((x) & 0x3f) << 26)
#define PDR0_CSI_PRDF(x) (((x) & 0x7) << 23)
......
......@@ -1272,4 +1272,6 @@ static inline u32 get_pata_base (void)
#define CONFIG_SYS_MPC512x_USB_ADDR \
(CONFIG_SYS_IMMR + CONFIG_SYS_MPC512x_USB_OFFSET)
#define IIM_BASE_ADDR (CONFIG_SYS_IMMR + offsetof(immap_t, iim))
#endif /* __IMMAP_512x__ */
Driver implementing the fuse API for Freescale's IC Identification Module (IIM)
This IP can be found on the following SoCs:
- MPC512x,
- i.MX25,
- i.MX27,
- i.MX31,
- i.MX35,
- i.MX51,
- i.MX53.
The section numbers in this file refer to the i.MX25 Reference Manual.
A fuse word contains 8 fuse bit slots, as explained in 30.4.2.2.1.
A bank contains 256 fuse word slots, as shown by the memory map in 30.3.1.
Some fuse bit or word slots may not have the corresponding fuses actually
implemented in the fusebox.
See the README files of the SoCs using this driver in order to know the
conventions used by U-Boot to store some specific data in the fuses, e.g. MAC
addresses.
Fuse operations:
Read
Read operations are implemented as read accesses to the shadow registers,
using "Word y of Bank x" from the register summary in 30.3.2. This is
explained in detail in 30.4.5.1.
Sense
Sense operations are implemented as explained in 30.4.5.2.
Program
Program operations are implemented as explained in 30.4.5.3. Following
this operation, the shadow registers are reloaded by the hardware (not
immediately, but this does not make any difference for a user reading
these registers).
Override
Override operations are implemented as write accesses to the shadow
registers, as explained in 30.4.5.4.
Configuration:
CONFIG_FSL_IIM
Define this to enable the fsl_iim driver.
......@@ -28,6 +28,7 @@ LIB := $(obj)libmisc.o
COBJS-$(CONFIG_ALI152X) += ali512x.o
COBJS-$(CONFIG_DS4510) += ds4510.o
COBJS-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
COBJS-$(CONFIG_FSL_IIM) += fsl_iim.o
COBJS-$(CONFIG_GPIO_LED) += gpio_led.o
COBJS-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
COBJS-$(CONFIG_NS87308) += ns87308.o
......
/*
* (C) Copyright 2009-2013 ADVANSEE
* Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
*
* Based on the mpc512x iim code:
* Copyright 2008 Silicon Turnkey Express, Inc.
* Martha Marx <mmarx@silicontkx.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <fuse.h>
#include <asm/errno.h>
#include <asm/io.h>
#ifndef CONFIG_MPC512X
#include <asm/arch/imx-regs.h>
#endif
/* FSL IIM-specific constants */
#define STAT_BUSY 0x80
#define STAT_PRGD 0x02
#define STAT_SNSD 0x01
#define STATM_PRGD_M 0x02
#define STATM_SNSD_M 0x01
#define ERR_PRGE 0x80
#define ERR_WPE 0x40
#define ERR_OPE 0x20
#define ERR_RPE 0x10
#define ERR_WLRE 0x08
#define ERR_SNSE 0x04
#define ERR_PARITYE 0x02
#define EMASK_PRGE_M 0x80
#define EMASK_WPE_M 0x40
#define EMASK_OPE_M 0x20
#define EMASK_RPE_M 0x10
#define EMASK_WLRE_M 0x08
#define EMASK_SNSE_M 0x04
#define EMASK_PARITYE_M 0x02
#define FCTL_DPC 0x80
#define FCTL_PRG_LENGTH_MASK 0x70
#define FCTL_ESNS_N 0x08
#define FCTL_ESNS_0 0x04
#define FCTL_ESNS_1 0x02
#define FCTL_PRG 0x01
#define UA_A_BANK_MASK 0x38
#define UA_A_ROWH_MASK 0x07
#define LA_A_ROWL_MASK 0xf8
#define LA_A_BIT_MASK 0x07
#define PREV_PROD_REV_MASK 0xf8
#define PREV_PROD_VT_MASK 0x07
/* Select the correct accessors depending on endianness */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define iim_read32 in_le32
#define iim_write32 out_le32
#define iim_clrsetbits32 clrsetbits_le32
#define iim_clrbits32 clrbits_le32
#define iim_setbits32 setbits_le32
#elif __BYTE_ORDER == __BIG_ENDIAN
#define iim_read32 in_be32
#define iim_write32 out_be32
#define iim_clrsetbits32 clrsetbits_be32
#define iim_clrbits32 clrbits_be32
#define iim_setbits32 setbits_be32
#else
#error Endianess is not defined: please fix to continue
#endif
/* IIM control registers */
struct fsl_iim {
u32 stat;
u32 statm;
u32 err;
u32 emask;
u32 fctl;
u32 ua;
u32 la;
u32 sdat;
u32 prev;
u32 srev;
u32 prg_p;
u32 scs[0x1f5];
struct {
u32 word[0x100];
} bank[8];
};
static int prepare_access(struct fsl_iim **regs, u32 bank, u32 word, int assert,
const char *caller)
{
*regs = (struct fsl_iim *)IIM_BASE_ADDR;
if (bank >= ARRAY_SIZE((*regs)->bank) ||
word >= ARRAY_SIZE((*regs)->bank[0].word) ||
!assert) {
printf("fsl_iim %s(): Invalid argument\n", caller);
return -EINVAL;
}
return 0;
}
static void clear_status(struct fsl_iim *regs)
{
iim_setbits32(&regs->stat, 0);
iim_setbits32(&regs->err, 0);
}
static void finish_access(struct fsl_iim *regs, u32 *stat, u32 *err)
{
*stat = iim_read32(&regs->stat);
*err = iim_read32(&regs->err);
clear_status(regs);
}
static int prepare_read(struct fsl_iim **regs, u32 bank, u32 word, u32 *val,
const char *caller)
{
int ret;
ret = prepare_access(regs, bank, word, val != NULL, caller);
if (ret)
return ret;
clear_status(*regs);
return 0;
}
int fuse_read(u32 bank, u32 word, u32 *val)
{
struct fsl_iim *regs;
u32 stat, err;
int ret;
ret = prepare_read(&regs, bank, word, val, __func__);
if (ret)
return ret;
*val = iim_read32(&regs->bank[bank].word[word]);
finish_access(regs, &stat, &err);
if (err & ERR_RPE) {
puts("fsl_iim fuse_read(): Read protect error\n");
return -EIO;
}
return 0;
}
static void direct_access(struct fsl_iim *regs, u32 bank, u32 word, u32 bit,
u32 fctl, u32 *stat, u32 *err)
{
iim_write32(&regs->ua, bank << 3 | word >> 5);
iim_write32(&regs->la, (word << 3 | bit) & 0xff);
if (fctl == FCTL_PRG)
iim_write32(&regs->prg_p, 0xaa);
iim_setbits32(&regs->fctl, fctl);
while (iim_read32(&regs->stat) & STAT_BUSY)
udelay(20);
finish_access(regs, stat, err);
}
int fuse_sense(u32 bank, u32 word, u32 *val)
{
struct fsl_iim *regs;
u32 stat, err;
int ret;
ret = prepare_read(&regs, bank, word, val, __func__);
if (ret)
return ret;
direct_access(regs, bank, word, 0, FCTL_ESNS_N, &stat, &err);
if (err & ERR_SNSE) {
puts("fsl_iim fuse_sense(): Explicit sense cycle error\n");
return -EIO;
}
if (!(stat & STAT_SNSD)) {
puts("fsl_iim fuse_sense(): Explicit sense cycle did not complete\n");
return -EIO;
}
*val = iim_read32(&regs->sdat);
return 0;
}
static int prog_bit(struct fsl_iim *regs, u32 bank, u32 word, u32 bit)
{
u32 stat, err;
clear_status(regs);
direct_access(regs, bank, word, bit, FCTL_PRG, &stat, &err);
iim_write32(&regs->prg_p, 0x00);
if (err & ERR_PRGE) {
puts("fsl_iim fuse_prog(): Program error\n");
return -EIO;
}
if (err & ERR_WPE) {
puts("fsl_iim fuse_prog(): Write protect error\n");
return -EIO;
}
if (!(stat & STAT_PRGD)) {
puts("fsl_iim fuse_prog(): Program did not complete\n");
return -EIO;
}
return 0;
}
static int prepare_write(struct fsl_iim **regs, u32 bank, u32 word, u32 val,
const char *caller)
{
return prepare_access(regs, bank, word, !(val & ~0xff), caller);
}
int fuse_prog(u32 bank, u32 word, u32 val)
{
struct fsl_iim *regs;
u32 bit;
int ret;
ret = prepare_write(&regs, bank, word, val, __func__);
if (ret)
return ret;
for (bit = 0; val; bit++, val >>= 1)
if (val & 0x01) {
ret = prog_bit(regs, bank, word, bit);
if (ret)
return ret;
}
return 0;
}
int fuse_override(u32 bank, u32 word, u32 val)
{
struct fsl_iim *regs;
u32 stat, err;
int ret;
ret = prepare_write(&regs, bank, word, val, __func__);
if (ret)
return ret;
clear_status(regs);
iim_write32(&regs->bank[bank].word[word], val);
finish_access(regs, &stat, &err);
if (err & ERR_OPE) {
puts("fsl_iim fuse_override(): Override protect error\n");
return -EIO;
}
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment