1 | // SPDX-License-Identifier: GPL-2.0-only |
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_controller *host; |
24 | struct octeon_spi *p; |
25 | int ret; |
26 | |
27 | host = spi_alloc_host(dev, size: sizeof(struct octeon_spi)); |
28 | if (!host) |
29 | return -ENOMEM; |
30 | |
31 | p = spi_controller_get_devdata(ctlr: host); |
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, bar: 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_enabled(dev, NULL); |
53 | if (IS_ERR(ptr: p->clk)) { |
54 | ret = PTR_ERR(ptr: p->clk); |
55 | goto error; |
56 | } |
57 | |
58 | p->sys_freq = clk_get_rate(clk: 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 | |
63 | host->flags = SPI_CONTROLLER_HALF_DUPLEX; |
64 | host->num_chipselect = 4; |
65 | host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | |
66 | SPI_LSB_FIRST | SPI_3WIRE; |
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; |
71 | |
72 | pci_set_drvdata(pdev, data: host); |
73 | |
74 | ret = devm_spi_register_controller(dev, ctlr: host); |
75 | if (ret) |
76 | goto error; |
77 | |
78 | return 0; |
79 | |
80 | error: |
81 | pci_release_regions(pdev); |
82 | spi_controller_put(ctlr: host); |
83 | return ret; |
84 | } |
85 | |
86 | static void thunderx_spi_remove(struct pci_dev *pdev) |
87 | { |
88 | struct spi_controller *host = pci_get_drvdata(pdev); |
89 | struct octeon_spi *p; |
90 | |
91 | p = spi_controller_get_devdata(ctlr: host); |
92 | if (!p) |
93 | return; |
94 | |
95 | pci_release_regions(pdev); |
96 | /* Put everything in a known state. */ |
97 | writeq(val: 0, addr: 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" ); |
119 | |