1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Altera SPI driver |
4 | * |
5 | * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> |
6 | * |
7 | * Based on spi_s3c24xx.c, which is: |
8 | * Copyright (c) 2006 Ben Dooks |
9 | * Copyright (c) 2006 Simtec Electronics |
10 | * Ben Dooks <ben@simtec.co.uk> |
11 | */ |
12 | |
13 | #include <linux/interrupt.h> |
14 | #include <linux/errno.h> |
15 | #include <linux/module.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/spi/altera.h> |
18 | #include <linux/spi/spi.h> |
19 | #include <linux/io.h> |
20 | #include <linux/of.h> |
21 | |
22 | #define DRV_NAME "spi_altera" |
23 | |
24 | enum altera_spi_type { |
25 | ALTERA_SPI_TYPE_UNKNOWN, |
26 | ALTERA_SPI_TYPE_SUBDEV, |
27 | }; |
28 | |
29 | static const struct regmap_config spi_altera_config = { |
30 | .reg_bits = 32, |
31 | .reg_stride = 4, |
32 | .val_bits = 32, |
33 | .fast_io = true, |
34 | }; |
35 | |
36 | static int altera_spi_probe(struct platform_device *pdev) |
37 | { |
38 | const struct platform_device_id *platid = platform_get_device_id(pdev); |
39 | struct altera_spi_platform_data *pdata = dev_get_platdata(dev: &pdev->dev); |
40 | enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; |
41 | struct altera_spi *hw; |
42 | struct spi_controller *host; |
43 | int err = -ENODEV; |
44 | u16 i; |
45 | |
46 | host = spi_alloc_host(dev: &pdev->dev, size: sizeof(struct altera_spi)); |
47 | if (!host) |
48 | return err; |
49 | |
50 | /* setup the host state. */ |
51 | host->bus_num = -1; |
52 | |
53 | if (pdata) { |
54 | if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) { |
55 | dev_err(&pdev->dev, |
56 | "Invalid number of chipselect: %u\n" , |
57 | pdata->num_chipselect); |
58 | err = -EINVAL; |
59 | goto exit; |
60 | } |
61 | |
62 | host->num_chipselect = pdata->num_chipselect; |
63 | host->mode_bits = pdata->mode_bits; |
64 | host->bits_per_word_mask = pdata->bits_per_word_mask; |
65 | } else { |
66 | host->num_chipselect = 16; |
67 | host->mode_bits = SPI_CS_HIGH; |
68 | host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); |
69 | } |
70 | |
71 | host->dev.of_node = pdev->dev.of_node; |
72 | |
73 | hw = spi_controller_get_devdata(ctlr: host); |
74 | hw->dev = &pdev->dev; |
75 | |
76 | if (platid) |
77 | type = platid->driver_data; |
78 | |
79 | /* find and map our resources */ |
80 | if (type == ALTERA_SPI_TYPE_SUBDEV) { |
81 | struct resource *regoff; |
82 | |
83 | hw->regmap = dev_get_regmap(dev: pdev->dev.parent, NULL); |
84 | if (!hw->regmap) { |
85 | dev_err(&pdev->dev, "get regmap failed\n" ); |
86 | goto exit; |
87 | } |
88 | |
89 | regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); |
90 | if (regoff) |
91 | hw->regoff = regoff->start; |
92 | } else { |
93 | void __iomem *res; |
94 | |
95 | res = devm_platform_ioremap_resource(pdev, index: 0); |
96 | if (IS_ERR(ptr: res)) { |
97 | err = PTR_ERR(ptr: res); |
98 | goto exit; |
99 | } |
100 | |
101 | hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, |
102 | &spi_altera_config); |
103 | if (IS_ERR(ptr: hw->regmap)) { |
104 | dev_err(&pdev->dev, "regmap mmio init failed\n" ); |
105 | err = PTR_ERR(ptr: hw->regmap); |
106 | goto exit; |
107 | } |
108 | } |
109 | |
110 | altera_spi_init_host(host); |
111 | |
112 | /* irq is optional */ |
113 | hw->irq = platform_get_irq(pdev, 0); |
114 | if (hw->irq >= 0) { |
115 | err = devm_request_irq(dev: &pdev->dev, irq: hw->irq, handler: altera_spi_irq, irqflags: 0, |
116 | devname: pdev->name, dev_id: host); |
117 | if (err) |
118 | goto exit; |
119 | } |
120 | |
121 | err = devm_spi_register_controller(dev: &pdev->dev, ctlr: host); |
122 | if (err) |
123 | goto exit; |
124 | |
125 | if (pdata) { |
126 | for (i = 0; i < pdata->num_devices; i++) { |
127 | if (!spi_new_device(host, pdata->devices + i)) |
128 | dev_warn(&pdev->dev, |
129 | "unable to create SPI device: %s\n" , |
130 | pdata->devices[i].modalias); |
131 | } |
132 | } |
133 | |
134 | dev_info(&pdev->dev, "regoff %u, irq %d\n" , hw->regoff, hw->irq); |
135 | |
136 | return 0; |
137 | exit: |
138 | spi_controller_put(ctlr: host); |
139 | return err; |
140 | } |
141 | |
142 | #ifdef CONFIG_OF |
143 | static const struct of_device_id altera_spi_match[] = { |
144 | { .compatible = "ALTR,spi-1.0" , }, |
145 | { .compatible = "altr,spi-1.0" , }, |
146 | {}, |
147 | }; |
148 | MODULE_DEVICE_TABLE(of, altera_spi_match); |
149 | #endif /* CONFIG_OF */ |
150 | |
151 | static const struct platform_device_id altera_spi_ids[] = { |
152 | { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, |
153 | { "subdev_spi_altera" , ALTERA_SPI_TYPE_SUBDEV }, |
154 | { } |
155 | }; |
156 | MODULE_DEVICE_TABLE(platform, altera_spi_ids); |
157 | |
158 | static struct platform_driver altera_spi_driver = { |
159 | .probe = altera_spi_probe, |
160 | .driver = { |
161 | .name = DRV_NAME, |
162 | .pm = NULL, |
163 | .of_match_table = of_match_ptr(altera_spi_match), |
164 | }, |
165 | .id_table = altera_spi_ids, |
166 | }; |
167 | module_platform_driver(altera_spi_driver); |
168 | |
169 | MODULE_DESCRIPTION("Altera SPI driver" ); |
170 | MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>" ); |
171 | MODULE_LICENSE("GPL" ); |
172 | MODULE_ALIAS("platform:" DRV_NAME); |
173 | |