1b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
28eabdd1ec122cf6b77cf73e798f134fbace1b8d1Huang Shijie * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with
38eabdd1ec122cf6b77cf73e798f134fbace1b8d1Huang Shijie * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c
48eabdd1ec122cf6b77cf73e798f134fbace1b8d1Huang Shijie *
58eabdd1ec122cf6b77cf73e798f134fbace1b8d1Huang Shijie * Copyright (C) 2005, Intec Automation Inc.
68eabdd1ec122cf6b77cf73e798f134fbace1b8d1Huang Shijie * Copyright (C) 2014, Freescale Semiconductor, Inc.
7b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie *
8b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * This code is free software; you can redistribute it and/or modify
9b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * it under the terms of the GNU General Public License version 2 as
10b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * published by the Free Software Foundation.
11b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
12b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
13b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/err.h>
14b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/errno.h>
15b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/module.h>
16b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/device.h>
17b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/mutex.h>
18b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/math64.h>
19b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
20b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/mtd/cfi.h>
21b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/mtd/mtd.h>
22b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/of_platform.h>
23b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/spi/flash.h>
24b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#include <linux/mtd/spi-nor.h>
25b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
26b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/* Define max times to check status register before we give up. */
27b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define	MAX_READY_WAIT_JIFFIES	(40 * HZ) /* M25P16 specs 40s max chip erase */
28b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
29b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define JEDEC_MFR(_jedec_id)	((_jedec_id) >> 16)
30b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
3170f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchingsstatic const struct spi_device_id *spi_nor_match_id(const char *name);
3270f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchings
33b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
34b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Read the status register, returning its value in the location
35b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Return the status register value.
36b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Returns negative if error occurred.
37b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
38b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int read_sr(struct spi_nor *nor)
39b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
40b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
41b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u8 val;
42b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
43b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);
44b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret < 0) {
45b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		pr_err("error %d reading SR\n", (int) ret);
46b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
47b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
48b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
49b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return val;
50b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
51b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
52b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
53c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com * Read the flag status register, returning its value in the location
54c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com * Return the status register value.
55c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com * Returns negative if error occurred.
56c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com */
57c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.comstatic int read_fsr(struct spi_nor *nor)
58c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com{
59c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	int ret;
60c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	u8 val;
61c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
62c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);
63c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	if (ret < 0) {
64c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		pr_err("error %d reading FSR\n", ret);
65c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		return ret;
66c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	}
67c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
68c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	return val;
69c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com}
70c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
71c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com/*
72b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Read configuration register, returning its value in the
73b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * location. Return the configuration register value.
74b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Returns negative if error occured.
75b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
76b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int read_cr(struct spi_nor *nor)
77b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
78b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
79b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u8 val;
80b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
81b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
82b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret < 0) {
83b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		dev_err(nor->dev, "error %d reading CR\n", ret);
84b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
85b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
86b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
87b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return val;
88b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
89b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
90b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
91b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Dummy Cycle calculation for different type of read.
92b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * It can be used to support more commands with
93b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * different dummy cycle requirements.
94b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
95b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
96b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
97b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	switch (nor->flash_read) {
98b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_FAST:
99b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_DUAL:
100b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_QUAD:
101b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return 1;
102b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_NORMAL:
103b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return 0;
104b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
105b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return 0;
106b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
107b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
108b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
109b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Write status register 1 byte
110b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Returns negative if error occurred.
111b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
112b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic inline int write_sr(struct spi_nor *nor, u8 val)
113b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
114b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->cmd_buf[0] = val;
115b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
116b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
117b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
118b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
119b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Set write enable latch with Write Enable command.
120b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Returns negative if error occurred.
121b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
122b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic inline int write_enable(struct spi_nor *nor)
123b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
124b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
125b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
126b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
127b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
128b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Send write disble instruction to the chip.
129b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
130b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic inline int write_disable(struct spi_nor *nor)
131b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
132b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
133b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
134b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
135b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
136b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
137b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return mtd->priv;
138b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
139b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
140b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/* Enable/disable 4-byte addressing mode. */
141b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
142b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
143b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int status;
144b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	bool need_wren = false;
145b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u8 cmd;
146b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
147b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	switch (JEDEC_MFR(jedec_id)) {
148b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case CFI_MFR_ST: /* Micron, actually */
149b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* Some Micron need WREN command; all will accept it */
150b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		need_wren = true;
151b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case CFI_MFR_MACRONIX:
152b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case 0xEF /* winbond */:
153b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (need_wren)
154b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			write_enable(nor);
155b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
156b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
157b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status = nor->write_reg(nor, cmd, NULL, 0, 0);
158b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (need_wren)
159b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			write_disable(nor);
160b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
161b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return status;
162b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	default:
163b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* Spansion style */
164b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->cmd_buf[0] = enable << 7;
165b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
166b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
167b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
168b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
169b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_wait_till_ready(struct spi_nor *nor)
170b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
171b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	unsigned long deadline;
172b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int sr;
173b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
174b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	deadline = jiffies + MAX_READY_WAIT_JIFFIES;
175b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
176b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	do {
177b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		cond_resched();
178b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
179b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		sr = read_sr(nor);
180b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (sr < 0)
181b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			break;
182b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		else if (!(sr & SR_WIP))
183b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			return 0;
184b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} while (!time_after_eq(jiffies, deadline));
185b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
186b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return -ETIMEDOUT;
187b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
188b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
189c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.comstatic int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
190c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com{
191c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	unsigned long deadline;
192c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	int sr;
193c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	int fsr;
194c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
195c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	deadline = jiffies + MAX_READY_WAIT_JIFFIES;
196c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
197c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	do {
198c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		cond_resched();
199c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
200c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		sr = read_sr(nor);
201c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		if (sr < 0) {
202c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com			break;
203c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		} else if (!(sr & SR_WIP)) {
204c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com			fsr = read_fsr(nor);
205c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com			if (fsr < 0)
206c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com				break;
207c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com			if (fsr & FSR_READY)
208c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com				return 0;
209c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		}
210c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	} while (!time_after_eq(jiffies, deadline));
211c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
212c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	return -ETIMEDOUT;
213c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com}
214c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
215b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
216b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Service routine to read status register until ready, or timeout occurs.
217b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Returns non-zero if error.
218b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
219b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int wait_till_ready(struct spi_nor *nor)
220b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
221b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return nor->wait_till_ready(nor);
222b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
223b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
224b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
225b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Erase the whole flash memory
226b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie *
227b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Returns 0 if successful, non-zero otherwise.
228b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
229b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int erase_chip(struct spi_nor *nor)
230b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
231b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
232b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
233b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
234b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
235b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Wait until finished previous write command. */
236b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = wait_till_ready(nor);
237b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
238b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
239b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
240b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Send write enable, then erase commands. */
241b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	write_enable(nor);
242b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
243b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
244b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
245b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
246b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
247b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
248b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret = 0;
249b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
250b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mutex_lock(&nor->lock);
251b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
252b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (nor->prepare) {
253b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = nor->prepare(nor, ops);
254b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret) {
255b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			dev_err(nor->dev, "failed in the preparation.\n");
256b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			mutex_unlock(&nor->lock);
257b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			return ret;
258b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
259b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
260b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
261b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
262b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
263b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
264b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
265b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (nor->unprepare)
266b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->unprepare(nor, ops);
267b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mutex_unlock(&nor->lock);
268b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
269b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
270b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
271b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Erase an address range on the nor chip.  The address range may extend
272b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * one or more erase sectors.  Return an error is there is a problem erasing.
273b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
274b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
275b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
276b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct spi_nor *nor = mtd_to_spi_nor(mtd);
277b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u32 addr, len;
278b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	uint32_t rem;
279b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
280b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
281b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
282b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			(long long)instr->len);
283b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
284b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	div_u64_rem(instr->len, mtd->erasesize, &rem);
285b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (rem)
286b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return -EINVAL;
287b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
288b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	addr = instr->addr;
289b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	len = instr->len;
290b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
291b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE);
292b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
293b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
294b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
295b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* whole-chip erase? */
296b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (len == mtd->size) {
297b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (erase_chip(nor)) {
298b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			ret = -EIO;
299b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			goto erase_err;
300b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
301b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
302b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* REVISIT in some cases we could speed up erasing large regions
303b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	 * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K.  We may have set up
304b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * to use "small sector erase", but that's not always optimal.
305b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 */
306b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
307b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* "sector"-at-a-time erase */
308b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} else {
309b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		while (len) {
310b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			if (nor->erase(nor, addr)) {
311b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				ret = -EIO;
312b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				goto erase_err;
313b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			}
314b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
315b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			addr += mtd->erasesize;
316b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			len -= mtd->erasesize;
317b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
318b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
319b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
320b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
321b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
322b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	instr->state = MTD_ERASE_DONE;
323b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd_erase_callback(instr);
324b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
325b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
326b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
327b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijieerase_err:
328b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
329b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	instr->state = MTD_ERASE_FAILED;
330b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
331b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
332b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
333b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
334b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
335b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct spi_nor *nor = mtd_to_spi_nor(mtd);
336b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	uint32_t offset = ofs;
337b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	uint8_t status_old, status_new;
338b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret = 0;
339b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
340b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
341b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
342b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
343b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
344b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Wait until finished previous command */
345b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = wait_till_ready(nor);
346b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
347b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		goto err;
348b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
349b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	status_old = read_sr(nor);
350b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
351b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (offset < mtd->size - (mtd->size / 2))
352b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
353b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset < mtd->size - (mtd->size / 4))
354b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
355b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset < mtd->size - (mtd->size / 8))
356b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
357b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset < mtd->size - (mtd->size / 16))
358b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
359b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset < mtd->size - (mtd->size / 32))
360b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
361b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset < mtd->size - (mtd->size / 64))
362b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
363b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else
364b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
365b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
366b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Only modify protection if it will not unlock other areas */
367b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) >
368b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
369b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		write_enable(nor);
370b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = write_sr(nor, status_new);
371b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret)
372b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			goto err;
373b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
374b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
375b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijieerr:
376b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
377b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
378b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
379b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
380b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
381b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
382b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct spi_nor *nor = mtd_to_spi_nor(mtd);
383b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	uint32_t offset = ofs;
384b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	uint8_t status_old, status_new;
385b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret = 0;
386b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
387b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
388b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
389b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
390b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
391b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Wait until finished previous command */
392b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = wait_till_ready(nor);
393b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
394b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		goto err;
395b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
396b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	status_old = read_sr(nor);
397b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
398b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (offset+len > mtd->size - (mtd->size / 64))
399b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0);
400b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset+len > mtd->size - (mtd->size / 32))
401b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
402b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset+len > mtd->size - (mtd->size / 16))
403b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
404b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset+len > mtd->size - (mtd->size / 8))
405b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
406b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset+len > mtd->size - (mtd->size / 4))
407b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
408b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (offset+len > mtd->size - (mtd->size / 2))
409b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
410b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else
411b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
412b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
413b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Only modify protection if it will not lock other areas */
414b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) <
415b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
416b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		write_enable(nor);
417b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = write_sr(nor, status_new);
418b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret)
419b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			goto err;
420b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
421b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
422b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijieerr:
423b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
424b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
425b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
426b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
427b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestruct flash_info {
428b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* JEDEC id zero means "no ID" (most older chips); otherwise it has
429b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * a high byte of zero plus three data bytes: the manufacturer id,
430b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * then a two byte device id.
431b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 */
432b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u32		jedec_id;
433b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u16             ext_id;
434b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
435b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	/* The size listed here is what works with SPINOR_OP_SE, which isn't
436b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * necessarily called a "sector" by the vendor.
437b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 */
438b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	unsigned	sector_size;
439b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u16		n_sectors;
440b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
441b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u16		page_size;
442b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u16		addr_width;
443b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
444b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u16		flags;
445b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris#define	SECT_4K			0x01	/* SPINOR_OP_BE_4K works uniformly */
446b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define	SPI_NOR_NO_ERASE	0x02	/* No erase command needed */
447b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define	SST_WRITE		0x04	/* use SST byte programming */
448b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define	SPI_NOR_NO_FR		0x08	/* Can't do fastread */
449b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris#define	SECT_4K_PMC		0x10	/* SPINOR_OP_BE_4K_PMC works uniformly */
450b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define	SPI_NOR_DUAL_READ	0x20    /* Flash supports Dual Read */
451b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define	SPI_NOR_QUAD_READ	0x40    /* Flash supports Quad Read */
452c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com#define	USE_FSR			0x80	/* use flag status register */
453b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie};
454b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
455b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\
456b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	((kernel_ulong_t)&(struct flash_info) {				\
457b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.jedec_id = (_jedec_id),				\
458b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.ext_id = (_ext_id),					\
459b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.sector_size = (_sector_size),				\
460b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.n_sectors = (_n_sectors),				\
461b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.page_size = 256,					\
462b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.flags = (_flags),					\
463b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	})
464b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
465b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags)	\
466b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	((kernel_ulong_t)&(struct flash_info) {				\
467b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.sector_size = (_sector_size),				\
468b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.n_sectors = (_n_sectors),				\
469b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.page_size = (_page_size),				\
470b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.addr_width = (_addr_width),				\
471b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		.flags = (_flags),					\
472b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	})
473b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
474b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/* NOTE: double check command sets and memory organization when you add
475b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * more nor chips.  This current list focusses on newer chips, which
476b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * have been converging on command sets which including JEDEC ID.
477b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
478a5b7616c55e188fe3d6ef686bef402d4703ecb62Ben Hutchingsstatic const struct spi_device_id spi_nor_ids[] = {
479b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
480b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
481b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },
482b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
483b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },
484b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64, SECT_4K) },
485b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
486b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
487b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
488b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
489b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
490b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
491b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
492b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
493b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
494b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* EON -- en25xxx */
495b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "en25f32",    INFO(0x1c3116, 0, 64 * 1024,   64, SECT_4K) },
496b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "en25p32",    INFO(0x1c2016, 0, 64 * 1024,   64, 0) },
497b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "en25q32b",   INFO(0x1c3016, 0, 64 * 1024,   64, 0) },
498b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128, 0) },
499b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
500a41595b34ba40fb97345dfef85ea510981a2e17bSergey Ryazanov	{ "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, 0) },
501b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) },
502b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
503b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* ESMT */
504b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
505b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
506b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Everspin */
507b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
508b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
509b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
510b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* GigaDevice */
511b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) },
512b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
513b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
514b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Intel/Numonyx -- xxxs33b */
515b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) },
516b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) },
517b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) },
518b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
519b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Macronix */
520b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SECT_4K) },
521b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },
522b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) },
523b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SECT_4K) },
524b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, 0) },
525b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l3255e",  INFO(0xc29e16, 0, 64 * 1024,  64, SECT_4K) },
526b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) },
527b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
528b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
529b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
530b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
531b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
532b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
533b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
534b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Micron */
535b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
536b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
537b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, 0) },
538b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K) },
539b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
540c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	{ "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) },
541c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	{ "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },
542b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
543b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* PMC */
544b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },
545b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "pm25lv010",   INFO(0,        0, 32 * 1024,    4, SECT_4K_PMC) },
546b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "pm25lq032",   INFO(0x7f9d46, 0, 64 * 1024,   64, SECT_4K) },
547b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
548b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Spansion -- single (large) sector size only, at least
549b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * for the chips listed here (without boot sectors).
550b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 */
5519ab86995ed07f51c45ba1b549aff2bd6e46590e9Geert Uytterhoeven	{ "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
552b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, 0) },
553b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
554b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
555b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
556b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
557b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
558b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
559b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
560b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
561b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
562b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },
563b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
564b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
565b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
566b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K) },
567b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) },
568b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
569b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
570b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
571b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
572b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
573b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
574b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
575b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
576b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K | SST_WRITE) },
577b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) },
578b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) },
579b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
580b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
581b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* ST Microelectronics -- newer production may have feature updates */
582b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
583b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },
584b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },
585b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
586b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },
587b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },
588b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
589b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
590b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
591b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "n25q032", INFO(0x20ba16,  0,  64 * 1024,  64, 0) },
592b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
593b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) },
594b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) },
595b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, 0) },
596b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, 0) },
597b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, 0) },
598b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, 0) },
599b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, 0) },
600b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, 0) },
601b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, 0) },
602b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
603b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) },
604b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) },
605b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) },
606b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
607b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25pe20", INFO(0x208012,  0, 64 * 1024,  4,       0) },
608b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) },
609b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },
610b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
611b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25px16",    INFO(0x207115,  0, 64 * 1024, 32, SECT_4K) },
612b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25px32",    INFO(0x207116,  0, 64 * 1024, 64, SECT_4K) },
613b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SECT_4K) },
614b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SECT_4K) },
615b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) },
616f2fabe16b819cdead86fb38c8ab88a0d9c308293Thomas Petazzoni	{ "m25px80",    INFO(0x207114,  0, 64 * 1024, 16, 0) },
617b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
618b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
619b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },
620b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) },
621b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },
622b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },
623b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) },
624b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },
625b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) },
626b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64, SECT_4K) },
627b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
628b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
629b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
630b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
631b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
632b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
633b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
634b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Catalyst / On Semiconductor -- non-JEDEC */
635b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "cat25c11", CAT25_INFO(  16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
636b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "cat25c03", CAT25_INFO(  32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
637b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
638b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
639b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
640b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	{ },
641b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie};
642b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
643b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
644b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
645b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int			tmp;
646b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u8			id[5];
647b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u32			jedec;
648b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u16                     ext_jedec;
649b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct flash_info	*info;
650b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
651b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5);
652b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (tmp < 0) {
653b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
654b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ERR_PTR(tmp);
655b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
656b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	jedec = id[0];
657b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	jedec = jedec << 8;
658b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	jedec |= id[1];
659b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	jedec = jedec << 8;
660b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	jedec |= id[2];
661b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
662b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ext_jedec = id[3] << 8 | id[4];
663b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
664b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
665b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		info = (void *)spi_nor_ids[tmp].driver_data;
666b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (info->jedec_id == jedec) {
667b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			if (info->ext_id == 0 || info->ext_id == ext_jedec)
668b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				return &spi_nor_ids[tmp];
669b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
670b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
671b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
672b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ERR_PTR(-ENODEV);
673b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
674b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
675b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
676b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			size_t *retlen, u_char *buf)
677b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
678b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct spi_nor *nor = mtd_to_spi_nor(mtd);
679b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
680b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
681b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
682b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
683b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);
684b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
685b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
686b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
687b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = nor->read(nor, from, len, retlen, buf);
688b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
689b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
690b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
691b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
692b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
693b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
694b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		size_t *retlen, const u_char *buf)
695b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
696b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct spi_nor *nor = mtd_to_spi_nor(mtd);
697b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	size_t actual;
698b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
699b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
700b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
701b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
702b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
703b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
704b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
705b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
706b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Wait until finished previous write command. */
707b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = wait_till_ready(nor);
708b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
709b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		goto time_out;
710b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
711b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	write_enable(nor);
712b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
713b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->sst_write_second = false;
714b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
715b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	actual = to % 2;
716b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Start write from odd address. */
717b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (actual) {
718b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		nor->program_opcode = SPINOR_OP_BP;
719b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
720b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* write one byte. */
721b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->write(nor, to, 1, retlen, buf);
722b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = wait_till_ready(nor);
723b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret)
724b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			goto time_out;
725b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
726b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	to += actual;
727b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
728b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Write out most of the data here. */
729b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	for (; actual < len - 1; actual += 2) {
730b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		nor->program_opcode = SPINOR_OP_AAI_WP;
731b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
732b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* write two bytes. */
733b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->write(nor, to, 2, retlen, buf + actual);
734b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = wait_till_ready(nor);
735b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret)
736b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			goto time_out;
737b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		to += 2;
738b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->sst_write_second = true;
739b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
740b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->sst_write_second = false;
741b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
742b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	write_disable(nor);
743b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = wait_till_ready(nor);
744b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
745b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		goto time_out;
746b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
747b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Write out trailing byte if it exists. */
748b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (actual != len) {
749b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		write_enable(nor);
750b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
751b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		nor->program_opcode = SPINOR_OP_BP;
752b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->write(nor, to, 1, retlen, buf + actual);
753b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
754b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = wait_till_ready(nor);
755b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret)
756b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			goto time_out;
757b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		write_disable(nor);
758b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
759b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijietime_out:
760b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
761b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return ret;
762b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
763b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
764b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
765b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Write an address range to the nor chip.  Data must be written in
766b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * FLASH_PAGESIZE chunks.  The address range may be any size provided
767b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * it is within the physical boundaries.
768b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
769b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
770b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	size_t *retlen, const u_char *buf)
771b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
772b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct spi_nor *nor = mtd_to_spi_nor(mtd);
773b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	u32 page_offset, page_size, i;
774b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
775b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
776b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
777b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
778b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
779b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
780b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
781b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
782b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Wait until finished previous write command. */
783b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = wait_till_ready(nor);
784b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
785b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		goto write_err;
786b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
787b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	write_enable(nor);
788b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
789b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	page_offset = to & (nor->page_size - 1);
790b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
791b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* do all the bytes fit onto one page? */
792b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (page_offset + len <= nor->page_size) {
793b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->write(nor, to, len, retlen, buf);
794b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} else {
795b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* the size of data remaining on the first page */
796b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		page_size = nor->page_size - page_offset;
797b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->write(nor, to, page_size, retlen, buf);
798b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
799b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* write everything in nor->page_size chunks */
800b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		for (i = page_size; i < len; i += page_size) {
801b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			page_size = len - i;
802b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			if (page_size > nor->page_size)
803b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				page_size = nor->page_size;
804b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
805b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			wait_till_ready(nor);
806b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			write_enable(nor);
807b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
808b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			nor->write(nor, to + i, page_size, retlen, buf + i);
809b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
810b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
811b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
812b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiewrite_err:
813b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
814b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return 0;
815b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
816b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
817b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int macronix_quad_enable(struct spi_nor *nor)
818b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
819b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret, val;
820b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
821b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	val = read_sr(nor);
822b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	write_enable(nor);
823b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
824b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
825b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
826b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
827b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (wait_till_ready(nor))
828b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return 1;
829b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
830b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = read_sr(nor);
831b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
832b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		dev_err(nor->dev, "Macronix Quad bit not set\n");
833b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return -EINVAL;
834b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
835b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
836b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return 0;
837b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
838b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
839b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie/*
840b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Write status Register and configuration register with 2 bytes
841b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * The first byte will be written to the status register, while the
842b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * second byte will be written to the configuration register.
843b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie * Return negative if error occured.
844b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie */
845b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int write_sr_cr(struct spi_nor *nor, u16 val)
846b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
847b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->cmd_buf[0] = val & 0xff;
848b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->cmd_buf[1] = (val >> 8);
849b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
850b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
851b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
852b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
853b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spansion_quad_enable(struct spi_nor *nor)
854b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
855b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
856b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int quad_en = CR_QUAD_EN_SPAN << 8;
857b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
858b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	write_enable(nor);
859b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
860b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = write_sr_cr(nor, quad_en);
861b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret < 0) {
862b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		dev_err(nor->dev,
863b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			"error while writing configuration register\n");
864b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return -EINVAL;
865b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
866b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
867b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* read back and check it */
868b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = read_cr(nor);
869b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
870b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		dev_err(nor->dev, "Spansion Quad bit not set\n");
871b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return -EINVAL;
872b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
873b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
874b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return 0;
875b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
876b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
877b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
878b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
879b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int status;
880b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
881b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	switch (JEDEC_MFR(jedec_id)) {
882b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case CFI_MFR_MACRONIX:
883b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status = macronix_quad_enable(nor);
884b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (status) {
885b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			dev_err(nor->dev, "Macronix quad-read not enabled\n");
886b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			return -EINVAL;
887b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
888b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return status;
889b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	default:
890b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		status = spansion_quad_enable(nor);
891b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (status) {
892b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			dev_err(nor->dev, "Spansion quad-read not enabled\n");
893b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			return -EINVAL;
894b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
895b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return status;
896b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
897b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
898b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
899b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijiestatic int spi_nor_check(struct spi_nor *nor)
900b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
901b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (!nor->dev || !nor->read || !nor->write ||
902b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		!nor->read_reg || !nor->write_reg || !nor->erase) {
903b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		pr_err("spi-nor: please fill all the necessary fields!\n");
904b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return -EINVAL;
905b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
906b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
907b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (!nor->read_id)
908b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->read_id = spi_nor_read_id;
909b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (!nor->wait_till_ready)
910b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->wait_till_ready = spi_nor_wait_till_ready;
911b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
912b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return 0;
913b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
914b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
91570f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchingsint spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
916b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie{
91770f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchings	const struct spi_device_id	*id = NULL;
918b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct flash_info		*info;
919b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct device *dev = nor->dev;
920b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct mtd_info *mtd = nor->mtd;
921b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	struct device_node *np = dev->of_node;
922b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int ret;
923b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	int i;
924b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
925b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	ret = spi_nor_check(nor);
926b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (ret)
927b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return ret;
928b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
92970f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchings	id = spi_nor_match_id(name);
93070f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchings	if (!id)
93170f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchings		return -ENOENT;
93270f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchings
933b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	info = (void *)id->driver_data;
934b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
935b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (info->jedec_id) {
936b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		const struct spi_device_id *jid;
937b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
93854ea17a597b00e46b3720e75dd7595cd5dfa5670Rafał Miłecki		jid = nor->read_id(nor);
939b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (IS_ERR(jid)) {
940b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			return PTR_ERR(jid);
941b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		} else if (jid != id) {
942b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			/*
943b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			 * JEDEC knows better, so overwrite platform ID. We
944b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			 * can't trust partitions any longer, but we'll let
945b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			 * mtd apply them anyway, since some partitions may be
946b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			 * marked read-only, and we don't want to lose that
947b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			 * information, even if it's not 100% accurate.
948b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			 */
949b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			dev_warn(dev, "found %s, expected %s\n",
950b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				 jid->name, id->name);
951b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			id = jid;
952b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			info = (void *)jid->driver_data;
953b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
954b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
955b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
956b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mutex_init(&nor->lock);
957b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
958b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/*
959b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * Atmel, SST and Intel/Numonyx serial nor tend to power
960b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 * up with the software protection bits set
961b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	 */
962b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
963b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
964b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	    JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
965b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	    JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
966b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		write_enable(nor);
967b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		write_sr(nor, 0);
968b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
969b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
97032f1b7c8352fd33d41bcec3cfb054ccdcfd40a42Rafał Miłecki	if (!mtd->name)
971b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->name = dev_name(dev);
972b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->type = MTD_NORFLASH;
973b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->writesize = 1;
974b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->flags = MTD_CAP_NORFLASH;
975b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->size = info->sector_size * info->n_sectors;
976b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->_erase = spi_nor_erase;
977b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->_read = spi_nor_read;
978b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
979b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* nor protection support for STmicro chips */
980b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
981b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->_lock = spi_nor_lock;
982b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->_unlock = spi_nor_unlock;
983b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
984b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
985b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* sst nor chips use AAI word program */
986b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (info->flags & SST_WRITE)
987b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->_write = sst_write;
988b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else
989b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->_write = spi_nor_write;
990b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
991c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	if ((info->flags & USE_FSR) &&
992c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com	    nor->wait_till_ready == spi_nor_wait_till_ready)
993c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com		nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
994c14deddec1fbd8c9757c53a49dbfd2dc83265f21grmoore@altera.com
99557cf26c1b28572976c57f6dec9818be38bf37cbbRafał Miłecki#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
996b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* prefer "small sector" erase if possible */
997b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (info->flags & SECT_4K) {
998b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		nor->erase_opcode = SPINOR_OP_BE_4K;
999b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->erasesize = 4096;
1000b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} else if (info->flags & SECT_4K_PMC) {
1001b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
1002b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->erasesize = 4096;
100357cf26c1b28572976c57f6dec9818be38bf37cbbRafał Miłecki	} else
100457cf26c1b28572976c57f6dec9818be38bf37cbbRafał Miłecki#endif
100557cf26c1b28572976c57f6dec9818be38bf37cbbRafał Miłecki	{
1006b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris		nor->erase_opcode = SPINOR_OP_SE;
1007b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->erasesize = info->sector_size;
1008b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
1009b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1010b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (info->flags & SPI_NOR_NO_ERASE)
1011b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->flags |= MTD_NO_ERASE;
1012b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1013b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->dev.parent = dev;
1014b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->page_size = info->page_size;
1015b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	mtd->writebufsize = nor->page_size;
1016b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1017b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (np) {
1018b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* If we were instantiated by DT, use it */
1019b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (of_property_read_bool(np, "m25p,fast-read"))
1020b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			nor->flash_read = SPI_NOR_FAST;
1021b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		else
1022b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			nor->flash_read = SPI_NOR_NORMAL;
1023b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} else {
1024b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* If we weren't instantiated by DT, default to fast-read */
1025b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->flash_read = SPI_NOR_FAST;
1026b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
1027b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1028b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Some devices cannot do fast-read, no matter what DT tells us */
1029b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (info->flags & SPI_NOR_NO_FR)
1030b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->flash_read = SPI_NOR_NORMAL;
1031b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1032b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Quad/Dual-read mode takes precedence over fast/normal */
1033b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
1034b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		ret = set_quad_mode(nor, info->jedec_id);
1035b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (ret) {
1036b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			dev_err(dev, "quad mode not supported\n");
1037b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			return ret;
1038b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		}
1039b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->flash_read = SPI_NOR_QUAD;
1040b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
1041b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->flash_read = SPI_NOR_DUAL;
1042b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
1043b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1044b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	/* Default commands */
1045b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	switch (nor->flash_read) {
1046b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_QUAD:
104758b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris		nor->read_opcode = SPINOR_OP_READ_1_1_4;
1048b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		break;
1049b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_DUAL:
105058b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris		nor->read_opcode = SPINOR_OP_READ_1_1_2;
1051b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		break;
1052b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_FAST:
105358b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris		nor->read_opcode = SPINOR_OP_READ_FAST;
1054b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		break;
1055b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	case SPI_NOR_NORMAL:
105658b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris		nor->read_opcode = SPINOR_OP_READ;
1057b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		break;
1058b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	default:
1059b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		dev_err(dev, "No Read opcode defined\n");
1060b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		return -EINVAL;
1061b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
1062b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1063b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris	nor->program_opcode = SPINOR_OP_PP;
1064b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1065b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (info->addr_width)
1066b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->addr_width = info->addr_width;
1067b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	else if (mtd->size > 0x1000000) {
1068b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		/* enable 4-byte addressing if the device exceeds 16MiB */
1069b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->addr_width = 4;
1070b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
1071b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			/* Dedicated 4-byte command set */
1072b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			switch (nor->flash_read) {
1073b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			case SPI_NOR_QUAD:
107458b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris				nor->read_opcode = SPINOR_OP_READ4_1_1_4;
1075b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				break;
1076b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			case SPI_NOR_DUAL:
107758b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris				nor->read_opcode = SPINOR_OP_READ4_1_1_2;
1078b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				break;
1079b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			case SPI_NOR_FAST:
108058b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris				nor->read_opcode = SPINOR_OP_READ4_FAST;
1081b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				break;
1082b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			case SPI_NOR_NORMAL:
108358b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1Brian Norris				nor->read_opcode = SPINOR_OP_READ4;
1084b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				break;
1085b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			}
1086b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris			nor->program_opcode = SPINOR_OP_PP_4B;
1087b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			/* No small sector erase for 4-byte command set */
1088b02e7f3ef0beb72da8fc64542f0ac977996ec56bBrian Norris			nor->erase_opcode = SPINOR_OP_SE_4B;
1089b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			mtd->erasesize = info->sector_size;
1090b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		} else
1091b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			set_4byte(nor, info->jedec_id, 1);
1092b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	} else {
1093b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		nor->addr_width = 3;
1094b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	}
1095b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1096b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
1097b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1098b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_info(dev, "%s (%lld Kbytes)\n", id->name,
1099b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			(long long)mtd->size >> 10);
1100b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1101b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	dev_dbg(dev,
1102b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		"mtd .name = %s, .size = 0x%llx (%lldMiB), "
1103b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
1104b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20),
1105b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions);
1106b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
1107b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	if (mtd->numeraseregions)
1108b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie		for (i = 0; i < mtd->numeraseregions; i++)
1109b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie			dev_dbg(dev,
1110b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				"mtd.eraseregions[%d] = { .offset = 0x%llx, "
1111b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				".erasesize = 0x%.8x (%uKiB), "
1112b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				".numblocks = %d }\n",
1113b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				i, (long long)mtd->eraseregions[i].offset,
1114b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				mtd->eraseregions[i].erasesize,
1115b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				mtd->eraseregions[i].erasesize / 1024,
1116b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie				mtd->eraseregions[i].numblocks);
1117b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie	return 0;
1118b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie}
1119b61834b0d0ed504b9340a55c88977cb9539454dfBrian NorrisEXPORT_SYMBOL_GPL(spi_nor_scan);
1120b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang Shijie
112170f3ce0510afdad7cbaf27ab7ab961377205c782Ben Hutchingsstatic const struct spi_device_id *spi_nor_match_id(const char *name)
11220d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie{
11230d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie	const struct spi_device_id *id = spi_nor_ids;
11240d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie
11250d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie	while (id->name[0]) {
11260d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie		if (!strcmp(name, id->name))
11270d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie			return id;
11280d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie		id++;
11290d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie	}
11300d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie	return NULL;
11310d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie}
11320d8c11c01274bde227d368daa8954911dd324a9fHuang Shijie
1133b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang ShijieMODULE_LICENSE("GPL");
1134b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang ShijieMODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");
1135b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang ShijieMODULE_AUTHOR("Mike Lavender");
1136b199489d37b21c5e294f95bf265acc5dde3fc3a2Huang ShijieMODULE_DESCRIPTION("framework for SPI NOR");
1137