/* * LPDDR2-NVM MTD driver. This module provides read, write, erase, lock/unlock * support for LPDDR2-NVM PCM memories * * Copyright © 2012 Micron Technology, Inc. * * Vincenzo Aliberti * Domenico Manna * Many thanks to Andrea Vigilante for initial enabling * * 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. */ #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include #include /* Parameters */ #define ERASE_BLOCKSIZE (0x00020000/2) /* in Word */ #define WRITE_BUFFSIZE (0x00000400/2) /* in Word */ #define OW_BASE_ADDRESS 0x00000000 /* OW offset */ #define BUS_WIDTH 0x00000020 /* x32 devices */ /* PFOW symbols address offset */ #define PFOW_QUERY_STRING_P (0x0000/2) /* in Word */ #define PFOW_QUERY_STRING_F (0x0002/2) /* in Word */ #define PFOW_QUERY_STRING_O (0x0004/2) /* in Word */ #define PFOW_QUERY_STRING_W (0x0006/2) /* in Word */ /* OW registers address */ #define CMD_CODE_OFS (0x0080/2) /* in Word */ #define CMD_DATA_OFS (0x0084/2) /* in Word */ #define CMD_ADD_L_OFS (0x0088/2) /* in Word */ #define CMD_ADD_H_OFS (0x008A/2) /* in Word */ #define MPR_L_OFS (0x0090/2) /* in Word */ #define MPR_H_OFS (0x0092/2) /* in Word */ #define CMD_EXEC_OFS (0x00C0/2) /* in Word */ #define STATUS_REG_OFS (0x00CC/2) /* in Word */ #define PRG_BUFFER_OFS (0x0010/2) /* in Word */ /* Datamask */ #define MR_CFGMASK 0x8000 #define SR_OK_DATAMASK 0x0080 /* LPDDR2-NVM Commands */ #define LPDDR2_NVM_LOCK 0x0061 #define LPDDR2_NVM_UNLOCK 0x0062 #define LPDDR2_NVM_SW_PROGRAM 0x0041 #define LPDDR2_NVM_SW_OVERWRITE 0x0042 #define LPDDR2_NVM_BUF_PROGRAM 0x00E9 #define LPDDR2_NVM_BUF_OVERWRITE 0x00EA #define LPDDR2_NVM_ERASE 0x0020 /* LPDDR2-NVM Registers offset */ #define LPDDR2_MODE_REG_DATA 0x0040 #define LPDDR2_MODE_REG_CFG 0x0050 /* * Internal Type Definitions * pcm_int_data contains memory controller details: * @reg_data : LPDDR2_MODE_REG_DATA register address after remapping * @reg_cfg : LPDDR2_MODE_REG_CFG register address after remapping * &bus_width: memory bus-width (eg: x16 2 Bytes, x32 4 Bytes) */ struct pcm_int_data { void __iomem *ctl_regs; int bus_width; }; static DEFINE_MUTEX(lpdd2_nvm_mutex); /* * Build a map_word starting from an u_long */ static inline map_word build_map_word(u_long myword) { map_word val = { {0} }; val.x[0] = myword; return val; } /* * Build Mode Register Configuration DataMask based on device bus-width */ static inline u_int build_mr_cfgmask(u_int bus_width) { u_int val = MR_CFGMASK; if (bus_width == 0x0004) /* x32 device */ val = val << 16; return val; } /* * Build Status Register OK DataMask based on device bus-width */ static inline u_int build_sr_ok_datamask(u_int bus_width) { u_int val = SR_OK_DATAMASK; if (bus_width == 0x0004) /* x32 device */ val = (val << 16)+val; return val; } /* * Evaluates Overlay Window Control Registers address */ static inline u_long ow_reg_add(struct map_info *map, u_long offset) { u_long val = 0; struct pcm_int_data *pcm_data = map->fldrv_priv; val = map->pfow_base + offset*pcm_data->bus_width; return val; } /* * Enable lpddr2-nvm Overlay Window * Overlay Window is a memory mapped area containing all LPDDR2-NVM registers * used by device commands as well as uservisible resources like Device Status * Register, Device ID, etc */ static inline void ow_enable(struct map_info *map) { struct pcm_int_data *pcm_data = map->fldrv_priv; writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18, pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG); writel_relaxed(0x01, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA); } /* * Disable lpddr2-nvm Overlay Window * Overlay Window is a memory mapped area containing all LPDDR2-NVM registers * used by device commands as well as uservisible resources like Device Status * Register, Device ID, etc */ static inline void ow_disable(struct map_info *map) { struct pcm_int_data *pcm_data = map->fldrv_priv; writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18, pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG); writel_relaxed(0x02, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA); } /* * Execute lpddr2-nvm operations */ static int lpddr2_nvm_do_op(struct map_info *map, u_long cmd_code, u_long cmd_data, u_long cmd_add, u_long cmd_mpr, u_char *buf) { map_word add_l = { {0} }, add_h = { {0} }, mpr_l = { {0} }, mpr_h = { {0} }, data_l = { {0} }, cmd = { {0} }, exec_cmd = { {0} }, sr; map_word data_h = { {0} }; /* only for 2x x16 devices stacked */ u_long i, status_reg, prg_buff_ofs; struct pcm_int_data *pcm_data = map->fldrv_priv; u_int sr_ok_datamask = build_sr_ok_datamask(pcm_data->bus_width); /* Builds low and high words for OW Control Registers */ add_l.x[0] = cmd_add & 0x0000FFFF; add_h.x[0] = (cmd_add >> 16) & 0x0000FFFF; mpr_l.x[0] = cmd_mpr & 0x0000FFFF; mpr_h.x[0] = (cmd_mpr >> 16) & 0x0000FFFF; cmd.x[0] = cmd_code & 0x0000FFFF; exec_cmd.x[0] = 0x0001; data_l.x[0] = cmd_data & 0x0000FFFF; data_h.x[0] = (cmd_