gpio-thunderx.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /*
  2. * This file is subject to the terms and conditions of the GNU General Public
  3. * License. See the file "COPYING" in the main directory of this archive
  4. * for more details.
  5. *
  6. * Copyright (C) 2016, 2017 Cavium Inc.
  7. */
  8. #include <linux/bitops.h>
  9. #include <linux/gpio/driver.h>
  10. #include <linux/interrupt.h>
  11. #include <linux/io.h>
  12. #include <linux/irq.h>
  13. #include <linux/kernel.h>
  14. #include <linux/module.h>
  15. #include <linux/pci.h>
  16. #include <linux/spinlock.h>
  17. #define GPIO_RX_DAT 0x0
  18. #define GPIO_TX_SET 0x8
  19. #define GPIO_TX_CLR 0x10
  20. #define GPIO_CONST 0x90
  21. #define GPIO_CONST_GPIOS_MASK 0xff
  22. #define GPIO_BIT_CFG 0x400
  23. #define GPIO_BIT_CFG_TX_OE BIT(0)
  24. #define GPIO_BIT_CFG_PIN_XOR BIT(1)
  25. #define GPIO_BIT_CFG_INT_EN BIT(2)
  26. #define GPIO_BIT_CFG_INT_TYPE BIT(3)
  27. #define GPIO_BIT_CFG_FIL_MASK GENMASK(11, 4)
  28. #define GPIO_BIT_CFG_FIL_CNT_SHIFT 4
  29. #define GPIO_BIT_CFG_FIL_SEL_SHIFT 8
  30. #define GPIO_BIT_CFG_TX_OD BIT(12)
  31. #define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK(25, 16)
  32. #define GPIO_INTR 0x800
  33. #define GPIO_INTR_INTR BIT(0)
  34. #define GPIO_INTR_INTR_W1S BIT(1)
  35. #define GPIO_INTR_ENA_W1C BIT(2)
  36. #define GPIO_INTR_ENA_W1S BIT(3)
  37. #define GPIO_2ND_BANK 0x1400
  38. #define GLITCH_FILTER_400NS ((4u << GPIO_BIT_CFG_FIL_SEL_SHIFT) | \
  39. (9u << GPIO_BIT_CFG_FIL_CNT_SHIFT))
  40. struct thunderx_gpio;
  41. struct thunderx_line {
  42. struct thunderx_gpio *txgpio;
  43. unsigned int line;
  44. unsigned int fil_bits;
  45. };
  46. struct thunderx_gpio {
  47. struct gpio_chip chip;
  48. u8 __iomem *register_base;
  49. struct msix_entry *msix_entries; /* per line MSI-X */
  50. struct thunderx_line *line_entries; /* per line irq info */
  51. raw_spinlock_t lock;
  52. unsigned long invert_mask[2];
  53. unsigned long od_mask[2];
  54. int base_msi;
  55. };
  56. static unsigned int bit_cfg_reg(unsigned int line)
  57. {
  58. return 8 * line + GPIO_BIT_CFG;
  59. }
  60. static unsigned int intr_reg(unsigned int line)
  61. {
  62. return 8 * line + GPIO_INTR;
  63. }
  64. static bool thunderx_gpio_is_gpio_nowarn(struct thunderx_gpio *txgpio,
  65. unsigned int line)
  66. {
  67. u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
  68. return (bit_cfg & GPIO_BIT_CFG_PIN_SEL_MASK) == 0;
  69. }
  70. /*
  71. * Check (and WARN) that the pin is available for GPIO. We will not
  72. * allow modification of the state of non-GPIO pins from this driver.
  73. */
  74. static bool thunderx_gpio_is_gpio(struct thunderx_gpio *txgpio,
  75. unsigned int line)
  76. {
  77. bool rv = thunderx_gpio_is_gpio_nowarn(txgpio, line);
  78. WARN_RATELIMIT(!rv, "Pin %d not available for GPIO\n", line);
  79. return rv;
  80. }
  81. static int thunderx_gpio_request(struct gpio_chip *chip, unsigned int line)
  82. {
  83. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  84. return thunderx_gpio_is_gpio(txgpio, line) ? 0 : -EIO;
  85. }
  86. static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line)
  87. {
  88. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  89. if (!thunderx_gpio_is_gpio(txgpio, line))
  90. return -EIO;
  91. raw_spin_lock(&txgpio->lock);
  92. clear_bit(line, txgpio->invert_mask);
  93. clear_bit(line, txgpio->od_mask);
  94. writeq(txgpio->line_entries[line].fil_bits,
  95. txgpio->register_base + bit_cfg_reg(line));
  96. raw_spin_unlock(&txgpio->lock);
  97. return 0;
  98. }
  99. static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line,
  100. int value)
  101. {
  102. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  103. int bank = line / 64;
  104. int bank_bit = line % 64;
  105. void __iomem *reg = txgpio->register_base +
  106. (bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR);
  107. writeq(BIT_ULL(bank_bit), reg);
  108. }
  109. static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line,
  110. int value)
  111. {
  112. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  113. u64 bit_cfg = txgpio->line_entries[line].fil_bits | GPIO_BIT_CFG_TX_OE;
  114. if (!thunderx_gpio_is_gpio(txgpio, line))
  115. return -EIO;
  116. raw_spin_lock(&txgpio->lock);
  117. thunderx_gpio_set(chip, line, value);
  118. if (test_bit(line, txgpio->invert_mask))
  119. bit_cfg |= GPIO_BIT_CFG_PIN_XOR;
  120. if (test_bit(line, txgpio->od_mask))
  121. bit_cfg |= GPIO_BIT_CFG_TX_OD;
  122. writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line));
  123. raw_spin_unlock(&txgpio->lock);
  124. return 0;
  125. }
  126. static int thunderx_gpio_get_direction(struct gpio_chip *chip, unsigned int line)
  127. {
  128. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  129. u64 bit_cfg;
  130. if (!thunderx_gpio_is_gpio_nowarn(txgpio, line))
  131. /*
  132. * Say it is input for now to avoid WARNing on
  133. * gpiochip_add_data(). We will WARN if someone
  134. * requests it or tries to use it.
  135. */
  136. return 1;
  137. bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
  138. if (bit_cfg & GPIO_BIT_CFG_TX_OE)
  139. return GPIO_LINE_DIRECTION_OUT;
  140. return GPIO_LINE_DIRECTION_IN;
  141. }
  142. static int thunderx_gpio_set_config(struct gpio_chip *chip,
  143. unsigned int line,
  144. unsigned long cfg)
  145. {
  146. bool orig_invert, orig_od, orig_dat, new_invert, new_od;
  147. u32 arg, sel;
  148. u64 bit_cfg;
  149. int bank = line / 64;
  150. int bank_bit = line % 64;
  151. int ret = -ENOTSUPP;
  152. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  153. void __iomem *reg = txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET;
  154. if (!thunderx_gpio_is_gpio(txgpio, line))
  155. return -EIO;
  156. raw_spin_lock(&txgpio->lock);
  157. orig_invert = test_bit(line, txgpio->invert_mask);
  158. new_invert = orig_invert;
  159. orig_od = test_bit(line, txgpio->od_mask);
  160. new_od = orig_od;
  161. orig_dat = ((readq(reg) >> bank_bit) & 1) ^ orig_invert;
  162. bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line));
  163. switch (pinconf_to_config_param(cfg)) {
  164. case PIN_CONFIG_DRIVE_OPEN_DRAIN:
  165. /*
  166. * Weird, setting open-drain mode causes signal
  167. * inversion. Note this so we can compensate in the
  168. * dir_out function.
  169. */
  170. set_bit(line, txgpio->invert_mask);
  171. new_invert = true;
  172. set_bit(line, txgpio->od_mask);
  173. new_od = true;
  174. ret = 0;
  175. break;
  176. case PIN_CONFIG_DRIVE_PUSH_PULL:
  177. clear_bit(line, txgpio->invert_mask);
  178. new_invert = false;
  179. clear_bit(line, txgpio->od_mask);
  180. new_od = false;
  181. ret = 0;
  182. break;
  183. case PIN_CONFIG_INPUT_DEBOUNCE:
  184. arg = pinconf_to_config_argument(cfg);
  185. if (arg > 1228) { /* 15 * 2^15 * 2.5nS maximum */
  186. ret = -EINVAL;
  187. break;
  188. }
  189. arg *= 400; /* scale to 2.5nS clocks. */
  190. sel = 0;
  191. while (arg > 15) {
  192. sel++;
  193. arg++; /* always round up */
  194. arg >>= 1;
  195. }
  196. txgpio->line_entries[line].fil_bits =
  197. (sel << GPIO_BIT_CFG_FIL_SEL_SHIFT) |
  198. (arg << GPIO_BIT_CFG_FIL_CNT_SHIFT);
  199. bit_cfg &= ~GPIO_BIT_CFG_FIL_MASK;
  200. bit_cfg |= txgpio->line_entries[line].fil_bits;
  201. writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line));
  202. ret = 0;
  203. break;
  204. default:
  205. break;
  206. }
  207. raw_spin_unlock(&txgpio->lock);
  208. /*
  209. * If currently output and OPEN_DRAIN changed, install the new
  210. * settings
  211. */
  212. if ((new_invert != orig_invert || new_od != orig_od) &&
  213. (bit_cfg & GPIO_BIT_CFG_TX_OE))
  214. ret = thunderx_gpio_dir_out(chip, line, orig_dat ^ new_invert);
  215. return ret;
  216. }
  217. static int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line)
  218. {
  219. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  220. int bank = line / 64;
  221. int bank_bit = line % 64;
  222. u64 read_bits = readq(txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_RX_DAT);
  223. u64 masked_bits = read_bits & BIT_ULL(bank_bit);
  224. if (test_bit(line, txgpio->invert_mask))
  225. return masked_bits == 0;
  226. else
  227. return masked_bits != 0;
  228. }
  229. static void thunderx_gpio_set_multiple(struct gpio_chip *chip,
  230. unsigned long *mask,
  231. unsigned long *bits)
  232. {
  233. int bank;
  234. u64 set_bits, clear_bits;
  235. struct thunderx_gpio *txgpio = gpiochip_get_data(chip);
  236. for (bank = 0; bank <= chip->ngpio / 64; bank++) {
  237. set_bits = bits[bank] & mask[bank];
  238. clear_bits = ~bits[bank] & mask[bank];
  239. writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET);
  240. writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR);
  241. }
  242. }
  243. static void thunderx_gpio_irq_ack(struct irq_data *d)
  244. {
  245. struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  246. struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
  247. writeq(GPIO_INTR_INTR,
  248. txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
  249. }
  250. static void thunderx_gpio_irq_mask(struct irq_data *d)
  251. {
  252. struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  253. struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
  254. writeq(GPIO_INTR_ENA_W1C,
  255. txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
  256. }
  257. static void thunderx_gpio_irq_mask_ack(struct irq_data *d)
  258. {
  259. struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  260. struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
  261. writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR,
  262. txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
  263. }
  264. static void thunderx_gpio_irq_unmask(struct irq_data *d)
  265. {
  266. struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  267. struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
  268. writeq(GPIO_INTR_ENA_W1S,
  269. txgpio->register_base + intr_reg(irqd_to_hwirq(d)));
  270. }
  271. static int thunderx_gpio_irq_set_type(struct irq_data *d,
  272. unsigned int flow_type)
  273. {
  274. struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  275. struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
  276. struct thunderx_line *txline =
  277. &txgpio->line_entries[irqd_to_hwirq(d)];
  278. u64 bit_cfg;
  279. irqd_set_trigger_type(d, flow_type);
  280. bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN;
  281. if (flow_type & IRQ_TYPE_EDGE_BOTH) {
  282. irq_set_handler_locked(d, handle_fasteoi_ack_irq);
  283. bit_cfg |= GPIO_BIT_CFG_INT_TYPE;
  284. } else {
  285. irq_set_handler_locked(d, handle_fasteoi_mask_irq);
  286. }
  287. raw_spin_lock(&txgpio->lock);
  288. if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) {
  289. bit_cfg |= GPIO_BIT_CFG_PIN_XOR;
  290. set_bit(txline->line, txgpio->invert_mask);
  291. } else {
  292. clear_bit(txline->line, txgpio->invert_mask);
  293. }
  294. clear_bit(txline->line, txgpio->od_mask);
  295. writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(txline->line));
  296. raw_spin_unlock(&txgpio->lock);
  297. return IRQ_SET_MASK_OK;
  298. }
  299. static void thunderx_gpio_irq_enable(struct irq_data *data)
  300. {
  301. irq_chip_enable_parent(data);
  302. thunderx_gpio_irq_unmask(data);
  303. }
  304. static void thunderx_gpio_irq_disable(struct irq_data *data)
  305. {
  306. thunderx_gpio_irq_mask(data);
  307. irq_chip_disable_parent(data);
  308. }
  309. /*
  310. * Interrupts are chained from underlying MSI-X vectors. We have
  311. * these irq_chip functions to be able to handle level triggering
  312. * semantics and other acknowledgment tasks associated with the GPIO
  313. * mechanism.
  314. */
  315. static struct irq_chip thunderx_gpio_irq_chip = {
  316. .name = "GPIO",
  317. .irq_enable = thunderx_gpio_irq_enable,
  318. .irq_disable = thunderx_gpio_irq_disable,
  319. .irq_ack = thunderx_gpio_irq_ack,
  320. .irq_mask = thunderx_gpio_irq_mask,
  321. .irq_mask_ack = thunderx_gpio_irq_mask_ack,
  322. .irq_unmask = thunderx_gpio_irq_unmask,
  323. .irq_eoi = irq_chip_eoi_parent,
  324. .irq_set_affinity = irq_chip_set_affinity_parent,
  325. .irq_set_type = thunderx_gpio_irq_set_type,
  326. .flags = IRQCHIP_SET_TYPE_MASKED
  327. };
  328. static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
  329. unsigned int child,
  330. unsigned int child_type,
  331. unsigned int *parent,
  332. unsigned int *parent_type)
  333. {
  334. struct thunderx_gpio *txgpio = gpiochip_get_data(gc);
  335. struct irq_data *irqd;
  336. unsigned int irq;
  337. irq = txgpio->msix_entries[child].vector;
  338. irqd = irq_domain_get_irq_data(gc->irq.parent_domain, irq);
  339. if (!irqd)
  340. return -EINVAL;
  341. *parent = irqd_to_hwirq(irqd);
  342. *parent_type = IRQ_TYPE_LEVEL_HIGH;
  343. return 0;
  344. }
  345. static int thunderx_gpio_populate_parent_alloc_info(struct gpio_chip *chip,
  346. union gpio_irq_fwspec *gfwspec,
  347. unsigned int parent_hwirq,
  348. unsigned int parent_type)
  349. {
  350. msi_alloc_info_t *info = &gfwspec->msiinfo;
  351. info->hwirq = parent_hwirq;
  352. return 0;
  353. }
  354. static int thunderx_gpio_probe(struct pci_dev *pdev,
  355. const struct pci_device_id *id)
  356. {
  357. void __iomem * const *tbl;
  358. struct device *dev = &pdev->dev;
  359. struct thunderx_gpio *txgpio;
  360. struct gpio_chip *chip;
  361. struct gpio_irq_chip *girq;
  362. int ngpio, i;
  363. int err = 0;
  364. txgpio = devm_kzalloc(dev, sizeof(*txgpio), GFP_KERNEL);
  365. if (!txgpio)
  366. return -ENOMEM;
  367. raw_spin_lock_init(&txgpio->lock);
  368. chip = &txgpio->chip;
  369. pci_set_drvdata(pdev, txgpio);
  370. err = pcim_enable_device(pdev);
  371. if (err) {
  372. dev_err(dev, "Failed to enable PCI device: err %d\n", err);
  373. goto out;
  374. }
  375. err = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
  376. if (err) {
  377. dev_err(dev, "Failed to iomap PCI device: err %d\n", err);
  378. goto out;
  379. }
  380. tbl = pcim_iomap_table(pdev);
  381. txgpio->register_base = tbl[0];
  382. if (!txgpio->register_base) {
  383. dev_err(dev, "Cannot map PCI resource\n");
  384. err = -ENOMEM;
  385. goto out;
  386. }
  387. if (pdev->subsystem_device == 0xa10a) {
  388. /* CN88XX has no GPIO_CONST register*/
  389. ngpio = 50;
  390. txgpio->base_msi = 48;
  391. } else {
  392. u64 c = readq(txgpio->register_base + GPIO_CONST);
  393. ngpio = c & GPIO_CONST_GPIOS_MASK;
  394. txgpio->base_msi = (c >> 8) & 0xff;
  395. }
  396. txgpio->msix_entries = devm_kcalloc(dev,
  397. ngpio, sizeof(struct msix_entry),
  398. GFP_KERNEL);
  399. if (!txgpio->msix_entries) {
  400. err = -ENOMEM;
  401. goto out;
  402. }
  403. txgpio->line_entries = devm_kcalloc(dev,
  404. ngpio,
  405. sizeof(struct thunderx_line),
  406. GFP_KERNEL);
  407. if (!txgpio->line_entries) {
  408. err = -ENOMEM;
  409. goto out;
  410. }
  411. for (i = 0; i < ngpio; i++) {
  412. u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(i));
  413. txgpio->msix_entries[i].entry = txgpio->base_msi + (2 * i);
  414. txgpio->line_entries[i].line = i;
  415. txgpio->line_entries[i].txgpio = txgpio;
  416. /*
  417. * If something has already programmed the pin, use
  418. * the existing glitch filter settings, otherwise go
  419. * to 400nS.
  420. */
  421. txgpio->line_entries[i].fil_bits = bit_cfg ?
  422. (bit_cfg & GPIO_BIT_CFG_FIL_MASK) : GLITCH_FILTER_400NS;
  423. if ((bit_cfg & GPIO_BIT_CFG_TX_OE) && (bit_cfg & GPIO_BIT_CFG_TX_OD))
  424. set_bit(i, txgpio->od_mask);
  425. if (bit_cfg & GPIO_BIT_CFG_PIN_XOR)
  426. set_bit(i, txgpio->invert_mask);
  427. }
  428. /* Enable all MSI-X for interrupts on all possible lines. */
  429. err = pci_enable_msix_range(pdev, txgpio->msix_entries, ngpio, ngpio);
  430. if (err < 0)
  431. goto out;
  432. chip->label = KBUILD_MODNAME;
  433. chip->parent = dev;
  434. chip->owner = THIS_MODULE;
  435. chip->request = thunderx_gpio_request;
  436. chip->base = -1; /* System allocated */
  437. chip->can_sleep = false;
  438. chip->ngpio = ngpio;
  439. chip->get_direction = thunderx_gpio_get_direction;
  440. chip->direction_input = thunderx_gpio_dir_in;
  441. chip->get = thunderx_gpio_get;
  442. chip->direction_output = thunderx_gpio_dir_out;
  443. chip->set = thunderx_gpio_set;
  444. chip->set_multiple = thunderx_gpio_set_multiple;
  445. chip->set_config = thunderx_gpio_set_config;
  446. girq = &chip->irq;
  447. girq->chip = &thunderx_gpio_irq_chip;
  448. girq->fwnode = of_node_to_fwnode(dev->of_node);
  449. girq->parent_domain =
  450. irq_get_irq_data(txgpio->msix_entries[0].vector)->domain;
  451. girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq;
  452. girq->populate_parent_alloc_arg = thunderx_gpio_populate_parent_alloc_info;
  453. girq->handler = handle_bad_irq;
  454. girq->default_type = IRQ_TYPE_NONE;
  455. err = devm_gpiochip_add_data(dev, chip, txgpio);
  456. if (err)
  457. goto out;
  458. /* Push on irq_data and the domain for each line. */
  459. for (i = 0; i < ngpio; i++) {
  460. struct irq_fwspec fwspec;
  461. fwspec.fwnode = of_node_to_fwnode(dev->of_node);
  462. fwspec.param_count = 2;
  463. fwspec.param[0] = i;
  464. fwspec.param[1] = IRQ_TYPE_NONE;
  465. err = irq_domain_push_irq(girq->domain,
  466. txgpio->msix_entries[i].vector,
  467. &fwspec);
  468. if (err < 0)
  469. dev_err(dev, "irq_domain_push_irq: %d\n", err);
  470. }
  471. dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n",
  472. ngpio, chip->base);
  473. return 0;
  474. out:
  475. pci_set_drvdata(pdev, NULL);
  476. return err;
  477. }
  478. static void thunderx_gpio_remove(struct pci_dev *pdev)
  479. {
  480. int i;
  481. struct thunderx_gpio *txgpio = pci_get_drvdata(pdev);
  482. for (i = 0; i < txgpio->chip.ngpio; i++)
  483. irq_domain_pop_irq(txgpio->chip.irq.domain,
  484. txgpio->msix_entries[i].vector);
  485. irq_domain_remove(txgpio->chip.irq.domain);
  486. pci_set_drvdata(pdev, NULL);
  487. }
  488. static const struct pci_device_id thunderx_gpio_id_table[] = {
  489. { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA00A) },
  490. { 0, } /* end of table */
  491. };
  492. MODULE_DEVICE_TABLE(pci, thunderx_gpio_id_table);
  493. static struct pci_driver thunderx_gpio_driver = {
  494. .name = KBUILD_MODNAME,
  495. .id_table = thunderx_gpio_id_table,
  496. .probe = thunderx_gpio_probe,
  497. .remove = thunderx_gpio_remove,
  498. };
  499. module_pci_driver(thunderx_gpio_driver);
  500. MODULE_DESCRIPTION("Cavium Inc. ThunderX/OCTEON-TX GPIO Driver");
  501. MODULE_LICENSE("GPL");