blob: c119486efb7ae326bc9f1169169b6f34279ececd [file] [log] [blame]
Frieder Schrempfea4d7c82018-08-16 17:30:14 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2017 exceet electronics GmbH
4 *
5 * Authors:
6 * Frieder Schrempf <frieder.schrempf@exceet.de>
7 * Boris Brezillon <boris.brezillon@bootlin.com>
8 */
9
10#ifndef __UBOOT__
Simon Glass9bc15642020-02-03 07:36:16 -070011#include <malloc.h>
Frieder Schrempfea4d7c82018-08-16 17:30:14 +020012#include <linux/device.h>
13#include <linux/kernel.h>
14#endif
Simon Glass4dcacfc2020-05-10 11:40:13 -060015#include <linux/bitops.h>
Frieder Schrempfea4d7c82018-08-16 17:30:14 +020016#include <linux/mtd/spinand.h>
17
18#define SPINAND_MFR_WINBOND 0xEF
19
20#define WINBOND_CFG_BUF_READ BIT(3)
21
22static SPINAND_OP_VARIANTS(read_cache_variants,
23 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
24 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
25 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
26 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
27 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
28 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
29
30static SPINAND_OP_VARIANTS(write_cache_variants,
31 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
32 SPINAND_PROG_LOAD(true, 0, NULL, 0));
33
34static SPINAND_OP_VARIANTS(update_cache_variants,
35 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
36 SPINAND_PROG_LOAD(false, 0, NULL, 0));
37
38static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
39 struct mtd_oob_region *region)
40{
41 if (section > 3)
42 return -ERANGE;
43
44 region->offset = (16 * section) + 8;
45 region->length = 8;
46
47 return 0;
48}
49
50static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
51 struct mtd_oob_region *region)
52{
53 if (section > 3)
54 return -ERANGE;
55
56 region->offset = (16 * section) + 2;
57 region->length = 6;
58
59 return 0;
60}
61
62static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
63 .ecc = w25m02gv_ooblayout_ecc,
Simon Glass62fd1a42020-02-03 07:35:56 -070064 .rfree = w25m02gv_ooblayout_free,
Frieder Schrempfea4d7c82018-08-16 17:30:14 +020065};
66
67static int w25m02gv_select_target(struct spinand_device *spinand,
68 unsigned int target)
69{
70 struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
71 SPI_MEM_OP_NO_ADDR,
72 SPI_MEM_OP_NO_DUMMY,
73 SPI_MEM_OP_DATA_OUT(1,
74 spinand->scratchbuf,
75 1));
76
77 *spinand->scratchbuf = target;
78 return spi_mem_exec_op(spinand->slave, &op);
79}
80
81static const struct spinand_info winbond_spinand_table[] = {
82 SPINAND_INFO("W25M02GV", 0xAB,
83 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
84 NAND_ECCREQ(1, 512),
85 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
86 &write_cache_variants,
87 &update_cache_variants),
88 0,
89 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
90 SPINAND_SELECT_TARGET(w25m02gv_select_target)),
Robert Markoa2ee9162020-01-16 14:03:35 +010091 SPINAND_INFO("W25N01GV", 0xAA,
92 NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
93 NAND_ECCREQ(1, 512),
94 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
95 &write_cache_variants,
96 &update_cache_variants),
97 0,
98 SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
Frieder Schrempfea4d7c82018-08-16 17:30:14 +020099};
100
101/**
102 * winbond_spinand_detect - initialize device related part in spinand_device
103 * struct if it is a Winbond device.
104 * @spinand: SPI NAND device structure
105 */
106static int winbond_spinand_detect(struct spinand_device *spinand)
107{
108 u8 *id = spinand->id.data;
109 int ret;
110
111 /*
112 * Winbond SPI NAND read ID need a dummy byte,
113 * so the first byte in raw_id is dummy.
114 */
115 if (id[1] != SPINAND_MFR_WINBOND)
116 return 0;
117
118 ret = spinand_match_and_init(spinand, winbond_spinand_table,
119 ARRAY_SIZE(winbond_spinand_table), id[2]);
120 if (ret)
121 return ret;
122
123 return 1;
124}
125
126static int winbond_spinand_init(struct spinand_device *spinand)
127{
128 struct nand_device *nand = spinand_to_nand(spinand);
129 unsigned int i;
130
131 /*
132 * Make sure all dies are in buffer read mode and not continuous read
133 * mode.
134 */
135 for (i = 0; i < nand->memorg.ntargets; i++) {
136 spinand_select_target(spinand, i);
137 spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ,
138 WINBOND_CFG_BUF_READ);
139 }
140
141 return 0;
142}
143
144static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
145 .detect = winbond_spinand_detect,
146 .init = winbond_spinand_init,
147};
148
149const struct spinand_manufacturer winbond_spinand_manufacturer = {
150 .id = SPINAND_MFR_WINBOND,
151 .name = "Winbond",
152 .ops = &winbond_spinand_manuf_ops,
153};