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; | |
23 | struct spi_master *master; | |
24 | struct octeon_spi *p; | |
25 | int ret; | |
26 | ||
27 | master = spi_alloc_master(dev, sizeof(struct octeon_spi)); | |
28 | if (!master) | |
29 | return -ENOMEM; | |
30 | ||
31 | p = spi_master_get_devdata(master); | |
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 | ||
52 | p->clk = devm_clk_get(dev, NULL); | |
53 | if (IS_ERR(p->clk)) { | |
54 | ret = PTR_ERR(p->clk); | |
55 | goto error; | |
56 | } | |
57 | ||
58 | ret = clk_prepare_enable(p->clk); | |
59 | if (ret) | |
60 | goto error; | |
61 | ||
62 | p->sys_freq = clk_get_rate(p->clk); | |
63 | if (!p->sys_freq) | |
64 | p->sys_freq = SYS_FREQ_DEFAULT; | |
65 | dev_info(dev, "Set system clock to %u\n", p->sys_freq); | |
66 | ||
e8510d43 | 67 | master->flags = SPI_MASTER_HALF_DUPLEX; |
7347a6c7 JG |
68 | master->num_chipselect = 4; |
69 | master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | | |
70 | SPI_LSB_FIRST | SPI_3WIRE; | |
71 | master->transfer_one_message = octeon_spi_transfer_one_message; | |
72 | master->bits_per_word_mask = SPI_BPW_MASK(8); | |
73 | master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; | |
74 | master->dev.of_node = pdev->dev.of_node; | |
75 | ||
76 | pci_set_drvdata(pdev, master); | |
77 | ||
78 | ret = devm_spi_register_master(dev, master); | |
79 | if (ret) | |
80 | goto error; | |
81 | ||
82 | return 0; | |
83 | ||
84 | error: | |
568852b7 | 85 | clk_disable_unprepare(p->clk); |
a841e285 | 86 | pci_release_regions(pdev); |
7347a6c7 JG |
87 | spi_master_put(master); |
88 | return ret; | |
89 | } | |
90 | ||
91 | static void thunderx_spi_remove(struct pci_dev *pdev) | |
92 | { | |
93 | struct spi_master *master = pci_get_drvdata(pdev); | |
94 | struct octeon_spi *p; | |
95 | ||
96 | p = spi_master_get_devdata(master); | |
97 | if (!p) | |
98 | return; | |
99 | ||
568852b7 | 100 | clk_disable_unprepare(p->clk); |
a841e285 | 101 | pci_release_regions(pdev); |
7347a6c7 JG |
102 | /* Put everything in a known state. */ |
103 | writeq(0, p->register_base + OCTEON_SPI_CFG(p)); | |
104 | } | |
105 | ||
106 | static const struct pci_device_id thunderx_spi_pci_id_table[] = { | |
107 | { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b) }, | |
108 | { 0, } | |
109 | }; | |
110 | ||
111 | MODULE_DEVICE_TABLE(pci, thunderx_spi_pci_id_table); | |
112 | ||
113 | static struct pci_driver thunderx_spi_driver = { | |
114 | .name = DRV_NAME, | |
115 | .id_table = thunderx_spi_pci_id_table, | |
116 | .probe = thunderx_spi_probe, | |
117 | .remove = thunderx_spi_remove, | |
118 | }; | |
119 | ||
120 | module_pci_driver(thunderx_spi_driver); | |
121 | ||
122 | MODULE_DESCRIPTION("Cavium, Inc. ThunderX SPI bus driver"); | |
123 | MODULE_AUTHOR("Jan Glauber"); | |
124 | MODULE_LICENSE("GPL"); |