1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Copyright (C) 2022 Hewlett-Packard Development Company, L.P. */ |
3 | |
4 | #include <linux/iopoll.h> |
5 | #include <linux/of.h> |
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(addr: reg_base + OFFSET_SPIMCTRL); |
56 | |
57 | if (mode == MANUAL_MODE) { |
58 | writeb(val: 0x55, addr: reg_base + OFFSET_SPICMD); |
59 | writeb(val: 0xaa, addr: reg_base + OFFSET_SPICMD); |
60 | value &= ~0x30; |
61 | } else { |
62 | value |= 0x30; |
63 | } |
64 | writeb(val: value, addr: 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(addr: reg_base + OFFSET_SPIMCFG); |
75 | value &= ~(1 << 24); |
76 | value |= (chip->cs << 24); |
77 | value &= ~(0x07 << 16); |
78 | value &= ~(0x1f << 19); |
79 | writel(val: value, addr: reg_base + OFFSET_SPIMCFG); |
80 | |
81 | writel(val: 0, addr: reg_base + OFFSET_SPIADDR); |
82 | |
83 | writeb(val: op->cmd.opcode, addr: reg_base + OFFSET_SPICMD); |
84 | |
85 | writew(val: op->data.nbytes, addr: reg_base + OFFSET_SPIDCNT); |
86 | |
87 | value = readb(addr: reg_base + OFFSET_SPIMCTRL); |
88 | value &= ~SPIMCTRL_DIR; |
89 | value |= SPIMCTRL_START; |
90 | |
91 | writeb(val: value, addr: 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(addr: reg_base + OFFSET_SPIMCFG); |
113 | value &= ~(1 << 24); |
114 | value |= (chip->cs << 24); |
115 | value &= ~(0x07 << 16); |
116 | value &= ~(0x1f << 19); |
117 | writel(val: value, addr: reg_base + OFFSET_SPIMCFG); |
118 | |
119 | writel(val: 0, addr: reg_base + OFFSET_SPIADDR); |
120 | |
121 | writeb(val: op->cmd.opcode, addr: reg_base + OFFSET_SPICMD); |
122 | |
123 | memcpy_toio(spifi->dat_base, op->data.buf.in, op->data.nbytes); |
124 | |
125 | writew(val: op->data.nbytes, addr: reg_base + OFFSET_SPIDCNT); |
126 | |
127 | value = readb(addr: reg_base + OFFSET_SPIMCTRL); |
128 | value |= SPIMCTRL_DIR; |
129 | value |= SPIMCTRL_START; |
130 | |
131 | writeb(val: value, addr: 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(addr: 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(val: value, addr: reg_base + OFFSET_SPIMCFG); |
174 | |
175 | writel(val: op->addr.val, addr: reg_base + OFFSET_SPIADDR); |
176 | |
177 | writeb(val: op->cmd.opcode, addr: reg_base + OFFSET_SPICMD); |
178 | |
179 | writew(val: write_len, addr: reg_base + OFFSET_SPIDCNT); |
180 | |
181 | memcpy_toio(spifi->dat_base, op->data.buf.in, write_len); |
182 | |
183 | value = readb(addr: reg_base + OFFSET_SPIMCTRL); |
184 | value |= SPIMCTRL_DIR; |
185 | value |= SPIMCTRL_START; |
186 | |
187 | writeb(val: value, addr: 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 0; |
198 | } |
199 | |
200 | static int do_gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) |
201 | { |
202 | struct gxp_spi *spifi = spi_controller_get_devdata(ctlr: mem->spi->controller); |
203 | struct gxp_spi_chip *chip = &spifi->chips[spi_get_chipselect(spi: mem->spi, idx: 0)]; |
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 | { |
238 | struct gxp_spi *spifi = spi_controller_get_devdata(ctlr: spi->controller); |
239 | unsigned int cs = spi_get_chipselect(spi, idx: 0); |
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; |
256 | int ret; |
257 | |
258 | data = of_device_get_match_data(dev: &pdev->dev); |
259 | |
260 | ctlr = devm_spi_alloc_host(dev, size: sizeof(*spifi)); |
261 | if (!ctlr) |
262 | return -ENOMEM; |
263 | |
264 | spifi = spi_controller_get_devdata(ctlr); |
265 | |
266 | platform_set_drvdata(pdev, data: spifi); |
267 | spifi->data = data; |
268 | spifi->dev = dev; |
269 | |
270 | spifi->reg_base = devm_platform_ioremap_resource(pdev, index: 0); |
271 | if (IS_ERR(ptr: spifi->reg_base)) |
272 | return PTR_ERR(ptr: spifi->reg_base); |
273 | |
274 | spifi->dat_base = devm_platform_ioremap_resource(pdev, index: 1); |
275 | if (IS_ERR(ptr: spifi->dat_base)) |
276 | return PTR_ERR(ptr: spifi->dat_base); |
277 | |
278 | spifi->dir_base = devm_platform_ioremap_resource(pdev, index: 2); |
279 | if (IS_ERR(ptr: spifi->dir_base)) |
280 | return PTR_ERR(ptr: 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(dev: &pdev->dev, err: ret, |
292 | fmt: "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" ); |
321 | |