]>
Commit | Line | Data |
---|---|---|
6465859a SG |
1 | /* |
2 | * Copyright (C) 2016 Cavium, Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of version 2 of the GNU General Public License | |
6 | * as published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/acpi.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/pci.h> | |
13 | #include <linux/netdevice.h> | |
14 | #include <linux/etherdevice.h> | |
15 | #include <linux/phy.h> | |
16 | #include <linux/of.h> | |
17 | #include <linux/of_mdio.h> | |
18 | #include <linux/of_net.h> | |
19 | ||
20 | #include "nic.h" | |
21 | #include "thunder_bgx.h" | |
22 | ||
23 | #define DRV_NAME "thunder-xcv" | |
24 | #define DRV_VERSION "1.0" | |
25 | ||
26 | /* Register offsets */ | |
27 | #define XCV_RESET 0x00 | |
28 | #define PORT_EN BIT_ULL(63) | |
29 | #define CLK_RESET BIT_ULL(15) | |
30 | #define DLL_RESET BIT_ULL(11) | |
31 | #define COMP_EN BIT_ULL(7) | |
32 | #define TX_PKT_RESET BIT_ULL(3) | |
33 | #define TX_DATA_RESET BIT_ULL(2) | |
34 | #define RX_PKT_RESET BIT_ULL(1) | |
35 | #define RX_DATA_RESET BIT_ULL(0) | |
36 | #define XCV_DLL_CTL 0x10 | |
37 | #define CLKRX_BYP BIT_ULL(23) | |
38 | #define CLKTX_BYP BIT_ULL(15) | |
39 | #define XCV_COMP_CTL 0x20 | |
40 | #define DRV_BYP BIT_ULL(63) | |
41 | #define XCV_CTL 0x30 | |
42 | #define XCV_INT 0x40 | |
43 | #define XCV_INT_W1S 0x48 | |
44 | #define XCV_INT_ENA_W1C 0x50 | |
45 | #define XCV_INT_ENA_W1S 0x58 | |
46 | #define XCV_INBND_STATUS 0x80 | |
47 | #define XCV_BATCH_CRD_RET 0x100 | |
48 | ||
49 | struct xcv { | |
50 | void __iomem *reg_base; | |
51 | struct pci_dev *pdev; | |
52 | }; | |
53 | ||
54 | static struct xcv *xcv; | |
55 | ||
56 | /* Supported devices */ | |
57 | static const struct pci_device_id xcv_id_table[] = { | |
58 | { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA056) }, | |
59 | { 0, } /* end of table */ | |
60 | }; | |
61 | ||
62 | MODULE_AUTHOR("Cavium Inc"); | |
63 | MODULE_DESCRIPTION("Cavium Thunder RGX/XCV Driver"); | |
64 | MODULE_LICENSE("GPL v2"); | |
65 | MODULE_VERSION(DRV_VERSION); | |
66 | MODULE_DEVICE_TABLE(pci, xcv_id_table); | |
67 | ||
68 | void xcv_init_hw(void) | |
69 | { | |
70 | u64 cfg; | |
71 | ||
72 | /* Take DLL out of reset */ | |
73 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
74 | cfg &= ~DLL_RESET; | |
75 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
76 | ||
77 | /* Take clock tree out of reset */ | |
78 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
79 | cfg &= ~CLK_RESET; | |
80 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
81 | /* Wait for DLL to lock */ | |
82 | msleep(1); | |
83 | ||
84 | /* Configure DLL - enable or bypass | |
85 | * TX no bypass, RX bypass | |
86 | */ | |
87 | cfg = readq_relaxed(xcv->reg_base + XCV_DLL_CTL); | |
88 | cfg &= ~0xFF03; | |
89 | cfg |= CLKRX_BYP; | |
90 | writeq_relaxed(cfg, xcv->reg_base + XCV_DLL_CTL); | |
91 | ||
92 | /* Enable compensation controller and force the | |
93 | * write to be visible to HW by readig back. | |
94 | */ | |
95 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
96 | cfg |= COMP_EN; | |
97 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
98 | readq_relaxed(xcv->reg_base + XCV_RESET); | |
99 | /* Wait for compensation state machine to lock */ | |
100 | msleep(10); | |
101 | ||
102 | /* enable the XCV block */ | |
103 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
104 | cfg |= PORT_EN; | |
105 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
106 | ||
107 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
108 | cfg |= CLK_RESET; | |
109 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
110 | } | |
111 | EXPORT_SYMBOL(xcv_init_hw); | |
112 | ||
113 | void xcv_setup_link(bool link_up, int link_speed) | |
114 | { | |
115 | u64 cfg; | |
116 | int speed = 2; | |
117 | ||
118 | if (!xcv) { | |
119 | dev_err(&xcv->pdev->dev, | |
120 | "XCV init not done, probe may have failed\n"); | |
121 | return; | |
122 | } | |
123 | ||
124 | if (link_speed == 100) | |
125 | speed = 1; | |
126 | else if (link_speed == 10) | |
127 | speed = 0; | |
128 | ||
129 | if (link_up) { | |
130 | /* set operating speed */ | |
131 | cfg = readq_relaxed(xcv->reg_base + XCV_CTL); | |
132 | cfg &= ~0x03; | |
133 | cfg |= speed; | |
134 | writeq_relaxed(cfg, xcv->reg_base + XCV_CTL); | |
135 | ||
136 | /* Reset datapaths */ | |
137 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
138 | cfg |= TX_DATA_RESET | RX_DATA_RESET; | |
139 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
140 | ||
141 | /* Enable the packet flow */ | |
142 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
143 | cfg |= TX_PKT_RESET | RX_PKT_RESET; | |
144 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
145 | ||
146 | /* Return credits to RGX */ | |
147 | writeq_relaxed(0x01, xcv->reg_base + XCV_BATCH_CRD_RET); | |
148 | } else { | |
149 | /* Disable packet flow */ | |
150 | cfg = readq_relaxed(xcv->reg_base + XCV_RESET); | |
151 | cfg &= ~(TX_PKT_RESET | RX_PKT_RESET); | |
152 | writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); | |
153 | readq_relaxed(xcv->reg_base + XCV_RESET); | |
154 | } | |
155 | } | |
156 | EXPORT_SYMBOL(xcv_setup_link); | |
157 | ||
158 | static int xcv_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |
159 | { | |
160 | int err; | |
161 | struct device *dev = &pdev->dev; | |
162 | ||
163 | xcv = devm_kzalloc(dev, sizeof(struct xcv), GFP_KERNEL); | |
164 | if (!xcv) | |
165 | return -ENOMEM; | |
166 | xcv->pdev = pdev; | |
167 | ||
168 | pci_set_drvdata(pdev, xcv); | |
169 | ||
170 | err = pci_enable_device(pdev); | |
171 | if (err) { | |
172 | dev_err(dev, "Failed to enable PCI device\n"); | |
173 | goto err_kfree; | |
174 | } | |
175 | ||
176 | err = pci_request_regions(pdev, DRV_NAME); | |
177 | if (err) { | |
178 | dev_err(dev, "PCI request regions failed 0x%x\n", err); | |
179 | goto err_disable_device; | |
180 | } | |
181 | ||
182 | /* MAP configuration registers */ | |
183 | xcv->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); | |
184 | if (!xcv->reg_base) { | |
185 | dev_err(dev, "XCV: Cannot map CSR memory space, aborting\n"); | |
186 | err = -ENOMEM; | |
187 | goto err_release_regions; | |
188 | } | |
189 | ||
190 | return 0; | |
191 | ||
192 | err_release_regions: | |
193 | pci_release_regions(pdev); | |
194 | err_disable_device: | |
195 | pci_disable_device(pdev); | |
196 | err_kfree: | |
6465859a SG |
197 | devm_kfree(dev, xcv); |
198 | xcv = NULL; | |
199 | return err; | |
200 | } | |
201 | ||
202 | static void xcv_remove(struct pci_dev *pdev) | |
203 | { | |
204 | struct device *dev = &pdev->dev; | |
205 | ||
206 | if (xcv) { | |
207 | devm_kfree(dev, xcv); | |
208 | xcv = NULL; | |
209 | } | |
210 | ||
211 | pci_release_regions(pdev); | |
212 | pci_disable_device(pdev); | |
6465859a SG |
213 | } |
214 | ||
215 | static struct pci_driver xcv_driver = { | |
216 | .name = DRV_NAME, | |
217 | .id_table = xcv_id_table, | |
218 | .probe = xcv_probe, | |
219 | .remove = xcv_remove, | |
220 | }; | |
221 | ||
222 | static int __init xcv_init_module(void) | |
223 | { | |
224 | pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION); | |
225 | ||
226 | return pci_register_driver(&xcv_driver); | |
227 | } | |
228 | ||
229 | static void __exit xcv_cleanup_module(void) | |
230 | { | |
231 | pci_unregister_driver(&xcv_driver); | |
232 | } | |
233 | ||
234 | module_init(xcv_init_module); | |
235 | module_exit(xcv_cleanup_module); |