Commit | Line | Data |
---|---|---|
033d2d13 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
730bc8ba NH |
2 | /* Copyright (C) 2022 Hewlett-Packard Development Company, L.P. */ |
3 | ||
4 | #include <linux/iopoll.h> | |
5 | #include <linux/of.h> | |
730bc8ba NH |
6 | #include <linux/platform_device.h> |
7 | #include <linux/spi/spi.h> | |
8 | #include <linux/spi/spi-mem.h> | |
9 | ||
10 | #define GXP_SPI0_MAX_CHIPSELECT 2 | |
11 | #define GXP_SPI_SLEEP_TIME 1 | |
12 | #define GXP_SPI_TIMEOUT (130 * 1000000 / GXP_SPI_SLEEP_TIME) | |
13 | ||
14 | #define MANUAL_MODE 0 | |
15 | #define DIRECT_MODE 1 | |
16 | #define SPILDAT_LEN 256 | |
17 | ||
18 | #define OFFSET_SPIMCFG 0x0 | |
19 | #define OFFSET_SPIMCTRL 0x4 | |
20 | #define OFFSET_SPICMD 0x5 | |
21 | #define OFFSET_SPIDCNT 0x6 | |
22 | #define OFFSET_SPIADDR 0x8 | |
23 | #define OFFSET_SPIINTSTS 0xc | |
24 | ||
25 | #define SPIMCTRL_START 0x01 | |
26 | #define SPIMCTRL_BUSY 0x02 | |
27 | #define SPIMCTRL_DIR 0x08 | |
28 | ||
29 | struct gxp_spi; | |
30 | ||
31 | struct gxp_spi_chip { | |
32 | struct gxp_spi *spifi; | |
33 | u32 cs; | |
34 | }; | |
35 | ||
36 | struct gxp_spi_data { | |
37 | u32 max_cs; | |
38 | u32 mode_bits; | |
39 | }; | |
40 | ||
41 | struct gxp_spi { | |
42 | const struct gxp_spi_data *data; | |
43 | void __iomem *reg_base; | |
44 | void __iomem *dat_base; | |
45 | void __iomem *dir_base; | |
46 | struct device *dev; | |
47 | struct gxp_spi_chip chips[GXP_SPI0_MAX_CHIPSELECT]; | |
48 | }; | |
49 | ||
50 | static void gxp_spi_set_mode(struct gxp_spi *spifi, int mode) | |
51 | { | |
52 | u8 value; | |
53 | void __iomem *reg_base = spifi->reg_base; | |
54 | ||
55 | value = readb(reg_base + OFFSET_SPIMCTRL); | |
56 | ||
57 | if (mode == MANUAL_MODE) { | |
58 | writeb(0x55, reg_base + OFFSET_SPICMD); | |
59 | writeb(0xaa, reg_base + OFFSET_SPICMD); | |
60 | value &= ~0x30; | |
61 | } else { | |
62 | value |= 0x30; | |
63 | } | |
64 | writeb(value, reg_base + OFFSET_SPIMCTRL); | |
65 | } | |
66 | ||
67 | static int gxp_spi_read_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op) | |
68 | { | |
69 | int ret; | |
70 | struct gxp_spi *spifi = chip->spifi; | |
71 | void __iomem *reg_base = spifi->reg_base; | |
72 | u32 value; | |
73 | ||
74 | value = readl(reg_base + OFFSET_SPIMCFG); | |
75 | value &= ~(1 << 24); | |
76 | value |= (chip->cs << 24); | |
77 | value &= ~(0x07 << 16); | |
78 | value &= ~(0x1f << 19); | |
79 | writel(value, reg_base + OFFSET_SPIMCFG); | |
80 | ||
81 | writel(0, reg_base + OFFSET_SPIADDR); | |
82 | ||
83 | writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD); | |
84 | ||
85 | writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT); | |
86 | ||
87 | value = readb(reg_base + OFFSET_SPIMCTRL); | |
88 | value &= ~SPIMCTRL_DIR; | |
89 | value |= SPIMCTRL_START; | |
90 | ||
91 | writeb(value, reg_base + OFFSET_SPIMCTRL); | |
92 | ||
93 | ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value, | |
94 | !(value & SPIMCTRL_BUSY), | |
95 | GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT); | |
96 | if (ret) { | |
97 | dev_warn(spifi->dev, "read reg busy time out\n"); | |
98 | return ret; | |
99 | } | |
100 | ||
101 | memcpy_fromio(op->data.buf.in, spifi->dat_base, op->data.nbytes); | |
102 | return ret; | |
103 | } | |
104 | ||
105 | static int gxp_spi_write_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op) | |
106 | { | |
107 | int ret; | |
108 | struct gxp_spi *spifi = chip->spifi; | |
109 | void __iomem *reg_base = spifi->reg_base; | |
110 | u32 value; | |
111 | ||
112 | value = readl(reg_base + OFFSET_SPIMCFG); | |
113 | value &= ~(1 << 24); | |
114 | value |= (chip->cs << 24); | |
115 | value &= ~(0x07 << 16); | |
116 | value &= ~(0x1f << 19); | |
117 | writel(value, reg_base + OFFSET_SPIMCFG); | |
118 | ||
119 | writel(0, reg_base + OFFSET_SPIADDR); | |
120 | ||
121 | writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD); | |
122 | ||
123 | memcpy_toio(spifi->dat_base, op->data.buf.in, op->data.nbytes); | |
124 | ||
125 | writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT); | |
126 | ||
127 | value = readb(reg_base + OFFSET_SPIMCTRL); | |
128 | value |= SPIMCTRL_DIR; | |
129 | value |= SPIMCTRL_START; | |
130 | ||
131 | writeb(value, reg_base + OFFSET_SPIMCTRL); | |
132 | ||
133 | ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value, | |
134 | !(value & SPIMCTRL_BUSY), | |
135 | GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT); | |
136 | if (ret) | |
137 | dev_warn(spifi->dev, "write reg busy time out\n"); | |
138 | ||
139 | return ret; | |
140 | } | |
141 | ||
142 | static ssize_t gxp_spi_read(struct gxp_spi_chip *chip, const struct spi_mem_op *op) | |
143 | { | |
144 | struct gxp_spi *spifi = chip->spifi; | |
145 | u32 offset = op->addr.val; | |
146 | ||
147 | if (chip->cs == 0) | |
148 | offset += 0x4000000; | |
149 | ||
150 | memcpy_fromio(op->data.buf.in, spifi->dir_base + offset, op->data.nbytes); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static ssize_t gxp_spi_write(struct gxp_spi_chip *chip, const struct spi_mem_op *op) | |
156 | { | |
157 | struct gxp_spi *spifi = chip->spifi; | |
158 | void __iomem *reg_base = spifi->reg_base; | |
159 | u32 write_len; | |
160 | u32 value; | |
161 | int ret; | |
162 | ||
163 | write_len = op->data.nbytes; | |
164 | if (write_len > SPILDAT_LEN) | |
165 | write_len = SPILDAT_LEN; | |
166 | ||
167 | value = readl(reg_base + OFFSET_SPIMCFG); | |
168 | value &= ~(1 << 24); | |
169 | value |= (chip->cs << 24); | |
170 | value &= ~(0x07 << 16); | |
171 | value |= (op->addr.nbytes << 16); | |
172 | value &= ~(0x1f << 19); | |
173 | writel(value, reg_base + OFFSET_SPIMCFG); | |
174 | ||
175 | writel(op->addr.val, reg_base + OFFSET_SPIADDR); | |
176 | ||
177 | writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD); | |
178 | ||
179 | writew(write_len, reg_base + OFFSET_SPIDCNT); | |
180 | ||
181 | memcpy_toio(spifi->dat_base, op->data.buf.in, write_len); | |
182 | ||
183 | value = readb(reg_base + OFFSET_SPIMCTRL); | |
184 | value |= SPIMCTRL_DIR; | |
185 | value |= SPIMCTRL_START; | |
186 | ||
187 | writeb(value, reg_base + OFFSET_SPIMCTRL); | |
188 | ||
189 | ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value, | |
190 | !(value & SPIMCTRL_BUSY), | |
191 | GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT); | |
192 | if (ret) { | |
193 | dev_warn(spifi->dev, "write busy time out\n"); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | return write_len; | |
198 | } | |
199 | ||
200 | static int do_gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) | |
201 | { | |
6588d43a | 202 | struct gxp_spi *spifi = spi_controller_get_devdata(mem->spi->controller); |
9e264f3f | 203 | struct gxp_spi_chip *chip = &spifi->chips[spi_get_chipselect(mem->spi, 0)]; |
730bc8ba NH |
204 | int ret; |
205 | ||
206 | if (op->data.dir == SPI_MEM_DATA_IN) { | |
207 | if (!op->addr.nbytes) | |
208 | ret = gxp_spi_read_reg(chip, op); | |
209 | else | |
210 | ret = gxp_spi_read(chip, op); | |
211 | } else { | |
212 | if (!op->addr.nbytes) | |
213 | ret = gxp_spi_write_reg(chip, op); | |
214 | else | |
215 | ret = gxp_spi_write(chip, op); | |
216 | } | |
217 | ||
218 | return ret; | |
219 | } | |
220 | ||
221 | static int gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) | |
222 | { | |
223 | int ret; | |
224 | ||
225 | ret = do_gxp_exec_mem_op(mem, op); | |
226 | if (ret) | |
227 | dev_err(&mem->spi->dev, "operation failed: %d", ret); | |
228 | ||
229 | return ret; | |
230 | } | |
231 | ||
232 | static const struct spi_controller_mem_ops gxp_spi_mem_ops = { | |
233 | .exec_op = gxp_exec_mem_op, | |
234 | }; | |
235 | ||
236 | static int gxp_spi_setup(struct spi_device *spi) | |
237 | { | |
6588d43a | 238 | struct gxp_spi *spifi = spi_controller_get_devdata(spi->controller); |
9e264f3f | 239 | unsigned int cs = spi_get_chipselect(spi, 0); |
730bc8ba NH |
240 | struct gxp_spi_chip *chip = &spifi->chips[cs]; |
241 | ||
242 | chip->spifi = spifi; | |
243 | chip->cs = cs; | |
244 | ||
245 | gxp_spi_set_mode(spifi, MANUAL_MODE); | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static int gxp_spifi_probe(struct platform_device *pdev) | |
251 | { | |
252 | struct device *dev = &pdev->dev; | |
253 | const struct gxp_spi_data *data; | |
254 | struct spi_controller *ctlr; | |
255 | struct gxp_spi *spifi; | |
730bc8ba NH |
256 | int ret; |
257 | ||
258 | data = of_device_get_match_data(&pdev->dev); | |
259 | ||
6588d43a | 260 | ctlr = devm_spi_alloc_host(dev, sizeof(*spifi)); |
730bc8ba NH |
261 | if (!ctlr) |
262 | return -ENOMEM; | |
263 | ||
264 | spifi = spi_controller_get_devdata(ctlr); | |
265 | ||
266 | platform_set_drvdata(pdev, spifi); | |
267 | spifi->data = data; | |
268 | spifi->dev = dev; | |
269 | ||
28366dd2 | 270 | spifi->reg_base = devm_platform_ioremap_resource(pdev, 0); |
730bc8ba NH |
271 | if (IS_ERR(spifi->reg_base)) |
272 | return PTR_ERR(spifi->reg_base); | |
273 | ||
28366dd2 | 274 | spifi->dat_base = devm_platform_ioremap_resource(pdev, 1); |
730bc8ba NH |
275 | if (IS_ERR(spifi->dat_base)) |
276 | return PTR_ERR(spifi->dat_base); | |
277 | ||
28366dd2 | 278 | spifi->dir_base = devm_platform_ioremap_resource(pdev, 2); |
730bc8ba NH |
279 | if (IS_ERR(spifi->dir_base)) |
280 | return PTR_ERR(spifi->dir_base); | |
281 | ||
282 | ctlr->mode_bits = data->mode_bits; | |
283 | ctlr->bus_num = pdev->id; | |
284 | ctlr->mem_ops = &gxp_spi_mem_ops; | |
285 | ctlr->setup = gxp_spi_setup; | |
286 | ctlr->num_chipselect = data->max_cs; | |
287 | ctlr->dev.of_node = dev->of_node; | |
288 | ||
289 | ret = devm_spi_register_controller(dev, ctlr); | |
290 | if (ret) { | |
291 | return dev_err_probe(&pdev->dev, ret, | |
292 | "failed to register spi controller\n"); | |
293 | } | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static const struct gxp_spi_data gxp_spifi_data = { | |
299 | .max_cs = 2, | |
300 | .mode_bits = 0, | |
301 | }; | |
302 | ||
303 | static const struct of_device_id gxp_spifi_match[] = { | |
304 | {.compatible = "hpe,gxp-spifi", .data = &gxp_spifi_data }, | |
305 | { /* null */ } | |
306 | }; | |
307 | MODULE_DEVICE_TABLE(of, gxp_spifi_match); | |
308 | ||
309 | static struct platform_driver gxp_spifi_driver = { | |
310 | .probe = gxp_spifi_probe, | |
311 | .driver = { | |
312 | .name = "gxp-spifi", | |
313 | .of_match_table = gxp_spifi_match, | |
314 | }, | |
315 | }; | |
316 | module_platform_driver(gxp_spifi_driver); | |
317 | ||
318 | MODULE_DESCRIPTION("HPE GXP SPI Flash Interface driver"); | |
319 | MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>"); | |
320 | MODULE_LICENSE("GPL"); |