Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7347a6c7 JG |
2 | /* |
3 | * Cavium ThunderX SPI driver. | |
4 | * | |
5 | * Copyright (C) 2016 Cavium Inc. | |
6 | * Authors: Jan Glauber <jglauber@cavium.com> | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/pci.h> | |
11 | #include <linux/spi/spi.h> | |
12 | ||
13 | #include "spi-cavium.h" | |
14 | ||
15 | #define DRV_NAME "spi-thunderx" | |
16 | ||
17 | #define SYS_FREQ_DEFAULT 700000000 /* 700 Mhz */ | |
18 | ||
19 | static int thunderx_spi_probe(struct pci_dev *pdev, | |
20 | const struct pci_device_id *ent) | |
21 | { | |
22 | struct device *dev = &pdev->dev; | |
556aca5b | 23 | struct spi_controller *host; |
7347a6c7 JG |
24 | struct octeon_spi *p; |
25 | int ret; | |
26 | ||
556aca5b YY |
27 | host = spi_alloc_host(dev, sizeof(struct octeon_spi)); |
28 | if (!host) | |
7347a6c7 JG |
29 | return -ENOMEM; |
30 | ||
556aca5b | 31 | p = spi_controller_get_devdata(host); |
7347a6c7 JG |
32 | |
33 | ret = pcim_enable_device(pdev); | |
34 | if (ret) | |
35 | goto error; | |
36 | ||
37 | ret = pci_request_regions(pdev, DRV_NAME); | |
38 | if (ret) | |
39 | goto error; | |
40 | ||
41 | p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); | |
42 | if (!p->register_base) { | |
43 | ret = -EINVAL; | |
44 | goto error; | |
45 | } | |
46 | ||
47 | p->regs.config = 0x1000; | |
48 | p->regs.status = 0x1008; | |
49 | p->regs.tx = 0x1010; | |
50 | p->regs.data = 0x1080; | |
51 | ||
c32cb76e | 52 | p->clk = devm_clk_get_enabled(dev, NULL); |
7347a6c7 JG |
53 | if (IS_ERR(p->clk)) { |
54 | ret = PTR_ERR(p->clk); | |
55 | goto error; | |
56 | } | |
57 | ||
7347a6c7 JG |
58 | p->sys_freq = clk_get_rate(p->clk); |
59 | if (!p->sys_freq) | |
60 | p->sys_freq = SYS_FREQ_DEFAULT; | |
61 | dev_info(dev, "Set system clock to %u\n", p->sys_freq); | |
62 | ||
556aca5b YY |
63 | host->flags = SPI_CONTROLLER_HALF_DUPLEX; |
64 | host->num_chipselect = 4; | |
65 | host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | | |
7347a6c7 | 66 | SPI_LSB_FIRST | SPI_3WIRE; |
556aca5b YY |
67 | host->transfer_one_message = octeon_spi_transfer_one_message; |
68 | host->bits_per_word_mask = SPI_BPW_MASK(8); | |
69 | host->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; | |
70 | host->dev.of_node = pdev->dev.of_node; | |
7347a6c7 | 71 | |
556aca5b | 72 | pci_set_drvdata(pdev, host); |
7347a6c7 | 73 | |
556aca5b | 74 | ret = devm_spi_register_controller(dev, host); |
7347a6c7 JG |
75 | if (ret) |
76 | goto error; | |
77 | ||
78 | return 0; | |
79 | ||
80 | error: | |
a841e285 | 81 | pci_release_regions(pdev); |
556aca5b | 82 | spi_controller_put(host); |
7347a6c7 JG |
83 | return ret; |
84 | } | |
85 | ||
86 | static void thunderx_spi_remove(struct pci_dev *pdev) | |
87 | { | |
556aca5b | 88 | struct spi_controller *host = pci_get_drvdata(pdev); |
7347a6c7 JG |
89 | struct octeon_spi *p; |
90 | ||
556aca5b | 91 | p = spi_controller_get_devdata(host); |
7347a6c7 JG |
92 | if (!p) |
93 | return; | |
94 | ||
a841e285 | 95 | pci_release_regions(pdev); |
7347a6c7 JG |
96 | /* Put everything in a known state. */ |
97 | writeq(0, p->register_base + OCTEON_SPI_CFG(p)); | |
98 | } | |
99 | ||
100 | static const struct pci_device_id thunderx_spi_pci_id_table[] = { | |
101 | { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b) }, | |
102 | { 0, } | |
103 | }; | |
104 | ||
105 | MODULE_DEVICE_TABLE(pci, thunderx_spi_pci_id_table); | |
106 | ||
107 | static struct pci_driver thunderx_spi_driver = { | |
108 | .name = DRV_NAME, | |
109 | .id_table = thunderx_spi_pci_id_table, | |
110 | .probe = thunderx_spi_probe, | |
111 | .remove = thunderx_spi_remove, | |
112 | }; | |
113 | ||
114 | module_pci_driver(thunderx_spi_driver); | |
115 | ||
116 | MODULE_DESCRIPTION("Cavium, Inc. ThunderX SPI bus driver"); | |
117 | MODULE_AUTHOR("Jan Glauber"); | |
118 | MODULE_LICENSE("GPL"); |