xref: /rk3399_rockchip-uboot/drivers/mtd/nand/spi/fmsh.c (revision 03d86fc3c0aa183eb9f49d9b07f71d253aebd3ec)
1*03d86fc3SJon Lin // SPDX-License-Identifier: GPL-2.0
2*03d86fc3SJon Lin /*
3*03d86fc3SJon Lin  * Copyright (c) 2020 exceet electronics GmbH
4*03d86fc3SJon Lin  *
5*03d86fc3SJon Lin  * Authors:
6*03d86fc3SJon Lin  *	Frieder Schrempf <frieder.schrempf@exceet.de>
7*03d86fc3SJon Lin  *	Boris Brezillon <boris.brezillon@bootlin.com>
8*03d86fc3SJon Lin  */
9*03d86fc3SJon Lin 
10*03d86fc3SJon Lin #ifndef __UBOOT__
11*03d86fc3SJon Lin #include <linux/device.h>
12*03d86fc3SJon Lin #include <linux/kernel.h>
13*03d86fc3SJon Lin #endif
14*03d86fc3SJon Lin #include <linux/mtd/spinand.h>
15*03d86fc3SJon Lin 
16*03d86fc3SJon Lin #define SPINAND_MFR_FMSH		0xA1
17*03d86fc3SJon Lin 
18*03d86fc3SJon Lin static SPINAND_OP_VARIANTS(read_cache_variants,
19*03d86fc3SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
20*03d86fc3SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
21*03d86fc3SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
22*03d86fc3SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
23*03d86fc3SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
24*03d86fc3SJon Lin 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
25*03d86fc3SJon Lin 
26*03d86fc3SJon Lin static SPINAND_OP_VARIANTS(write_cache_variants,
27*03d86fc3SJon Lin 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
28*03d86fc3SJon Lin 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
29*03d86fc3SJon Lin 
30*03d86fc3SJon Lin static SPINAND_OP_VARIANTS(update_cache_variants,
31*03d86fc3SJon Lin 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
32*03d86fc3SJon Lin 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
33*03d86fc3SJon Lin 
34*03d86fc3SJon Lin static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
35*03d86fc3SJon Lin 				  struct mtd_oob_region *region)
36*03d86fc3SJon Lin {
37*03d86fc3SJon Lin 	return -ERANGE;
38*03d86fc3SJon Lin }
39*03d86fc3SJon Lin 
40*03d86fc3SJon Lin static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section,
41*03d86fc3SJon Lin 				   struct mtd_oob_region *region)
42*03d86fc3SJon Lin {
43*03d86fc3SJon Lin 	if (section)
44*03d86fc3SJon Lin 		return -ERANGE;
45*03d86fc3SJon Lin 
46*03d86fc3SJon Lin 	region->offset = 2;
47*03d86fc3SJon Lin 	region->length = 62;
48*03d86fc3SJon Lin 
49*03d86fc3SJon Lin 	return 0;
50*03d86fc3SJon Lin }
51*03d86fc3SJon Lin 
52*03d86fc3SJon Lin static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
53*03d86fc3SJon Lin 	.ecc = fm25s01a_ooblayout_ecc,
54*03d86fc3SJon Lin 	.rfree = fm25s01a_ooblayout_free,
55*03d86fc3SJon Lin };
56*03d86fc3SJon Lin 
57*03d86fc3SJon Lin static const struct spinand_info fmsh_spinand_table[] = {
58*03d86fc3SJon Lin 	SPINAND_INFO("FM25S01A", 0xE4,
59*03d86fc3SJon Lin 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
60*03d86fc3SJon Lin 		     NAND_ECCREQ(1, 512),
61*03d86fc3SJon Lin 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
62*03d86fc3SJon Lin 					      &write_cache_variants,
63*03d86fc3SJon Lin 					      &update_cache_variants),
64*03d86fc3SJon Lin 		     0,
65*03d86fc3SJon Lin 		     SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
66*03d86fc3SJon Lin };
67*03d86fc3SJon Lin 
68*03d86fc3SJon Lin /**
69*03d86fc3SJon Lin  * fmsh_spinand_detect - initialize device related part in spinand_device
70*03d86fc3SJon Lin  * struct if it is a FMSH device.
71*03d86fc3SJon Lin  * @spinand: SPI NAND device structure
72*03d86fc3SJon Lin  */
73*03d86fc3SJon Lin static int fmsh_spinand_detect(struct spinand_device *spinand)
74*03d86fc3SJon Lin {
75*03d86fc3SJon Lin 	u8 *id = spinand->id.data;
76*03d86fc3SJon Lin 	int ret;
77*03d86fc3SJon Lin 
78*03d86fc3SJon Lin 	/*
79*03d86fc3SJon Lin 	 * FMSH SPI NAND read ID need a dummy byte,
80*03d86fc3SJon Lin 	 * so the first byte in raw_id is dummy.
81*03d86fc3SJon Lin 	 */
82*03d86fc3SJon Lin 	if (id[1] != SPINAND_MFR_FMSH)
83*03d86fc3SJon Lin 		return 0;
84*03d86fc3SJon Lin 
85*03d86fc3SJon Lin 	ret = spinand_match_and_init(spinand, fmsh_spinand_table,
86*03d86fc3SJon Lin 				     ARRAY_SIZE(fmsh_spinand_table), id[2]);
87*03d86fc3SJon Lin 	if (ret)
88*03d86fc3SJon Lin 		return ret;
89*03d86fc3SJon Lin 
90*03d86fc3SJon Lin 	return 1;
91*03d86fc3SJon Lin }
92*03d86fc3SJon Lin 
93*03d86fc3SJon Lin static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
94*03d86fc3SJon Lin 	.detect = fmsh_spinand_detect,
95*03d86fc3SJon Lin };
96*03d86fc3SJon Lin 
97*03d86fc3SJon Lin const struct spinand_manufacturer fmsh_spinand_manufacturer = {
98*03d86fc3SJon Lin 	.id = SPINAND_MFR_FMSH,
99*03d86fc3SJon Lin 	.name = "FMSH",
100*03d86fc3SJon Lin 	.ops = &fmsh_spinand_manuf_ops,
101*03d86fc3SJon Lin };
102