Commit | Line | Data |
---|---|---|
b0c3d935 MG |
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(&pdev->dev); | |
40 | enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; | |
41 | struct altera_spi *hw; | |
02c9e5b7 | 42 | struct spi_controller *host; |
b0c3d935 MG |
43 | int err = -ENODEV; |
44 | u16 i; | |
45 | ||
02c9e5b7 YY |
46 | host = spi_alloc_host(&pdev->dev, sizeof(struct altera_spi)); |
47 | if (!host) | |
b0c3d935 MG |
48 | return err; |
49 | ||
02c9e5b7 YY |
50 | /* setup the host state. */ |
51 | host->bus_num = -1; | |
b0c3d935 MG |
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 | ||
02c9e5b7 YY |
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; | |
b0c3d935 | 65 | } else { |
02c9e5b7 YY |
66 | host->num_chipselect = 16; |
67 | host->mode_bits = SPI_CS_HIGH; | |
68 | host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); | |
b0c3d935 MG |
69 | } |
70 | ||
02c9e5b7 | 71 | host->dev.of_node = pdev->dev.of_node; |
b0c3d935 | 72 | |
02c9e5b7 | 73 | hw = spi_controller_get_devdata(host); |
b0c3d935 MG |
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(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, 0); | |
96 | if (IS_ERR(res)) { | |
97 | err = PTR_ERR(res); | |
98 | goto exit; | |
99 | } | |
100 | ||
101 | hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, | |
102 | &spi_altera_config); | |
103 | if (IS_ERR(hw->regmap)) { | |
104 | dev_err(&pdev->dev, "regmap mmio init failed\n"); | |
105 | err = PTR_ERR(hw->regmap); | |
106 | goto exit; | |
107 | } | |
108 | } | |
109 | ||
02c9e5b7 | 110 | altera_spi_init_host(host); |
b0c3d935 MG |
111 | |
112 | /* irq is optional */ | |
113 | hw->irq = platform_get_irq(pdev, 0); | |
114 | if (hw->irq >= 0) { | |
115 | err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, | |
02c9e5b7 | 116 | pdev->name, host); |
b0c3d935 MG |
117 | if (err) |
118 | goto exit; | |
119 | } | |
120 | ||
02c9e5b7 | 121 | err = devm_spi_register_controller(&pdev->dev, host); |
b0c3d935 MG |
122 | if (err) |
123 | goto exit; | |
124 | ||
125 | if (pdata) { | |
126 | for (i = 0; i < pdata->num_devices; i++) { | |
02c9e5b7 | 127 | if (!spi_new_device(host, pdata->devices + i)) |
b0c3d935 MG |
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: | |
02c9e5b7 | 138 | spi_controller_put(host); |
b0c3d935 MG |
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); |