1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * VFIO platform driver specialized for AMD xgbe reset |
4 | * reset code is inherited from AMD xgbe native driver |
5 | * |
6 | * Copyright (c) 2015 Linaro Ltd. |
7 | * www.linaro.org |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> |
13 | #include <linux/io.h> |
14 | #include <uapi/linux/mdio.h> |
15 | #include <linux/delay.h> |
16 | |
17 | #include "../vfio_platform_private.h" |
18 | |
19 | #define DMA_MR 0x3000 |
20 | #define MAC_VR 0x0110 |
21 | #define DMA_ISR 0x3008 |
22 | #define MAC_ISR 0x00b0 |
23 | #define PCS_MMD_SELECT 0xff |
24 | #define MDIO_AN_INT 0x8002 |
25 | #define MDIO_AN_INTMASK 0x8001 |
26 | |
27 | static unsigned int xmdio_read(void __iomem *ioaddr, unsigned int mmd, |
28 | unsigned int reg) |
29 | { |
30 | unsigned int mmd_address, value; |
31 | |
32 | mmd_address = (mmd << 16) | ((reg) & 0xffff); |
33 | iowrite32(mmd_address >> 8, ioaddr + (PCS_MMD_SELECT << 2)); |
34 | value = ioread32(ioaddr + ((mmd_address & 0xff) << 2)); |
35 | return value; |
36 | } |
37 | |
38 | static void xmdio_write(void __iomem *ioaddr, unsigned int mmd, |
39 | unsigned int reg, unsigned int value) |
40 | { |
41 | unsigned int mmd_address; |
42 | |
43 | mmd_address = (mmd << 16) | ((reg) & 0xffff); |
44 | iowrite32(mmd_address >> 8, ioaddr + (PCS_MMD_SELECT << 2)); |
45 | iowrite32(value, ioaddr + ((mmd_address & 0xff) << 2)); |
46 | } |
47 | |
48 | static int vfio_platform_amdxgbe_reset(struct vfio_platform_device *vdev) |
49 | { |
50 | struct vfio_platform_region *xgmac_regs = &vdev->regions[0]; |
51 | struct vfio_platform_region *xpcs_regs = &vdev->regions[1]; |
52 | u32 dma_mr_value, pcs_value, value; |
53 | unsigned int count; |
54 | |
55 | if (!xgmac_regs->ioaddr) { |
56 | xgmac_regs->ioaddr = |
57 | ioremap(offset: xgmac_regs->addr, size: xgmac_regs->size); |
58 | if (!xgmac_regs->ioaddr) |
59 | return -ENOMEM; |
60 | } |
61 | if (!xpcs_regs->ioaddr) { |
62 | xpcs_regs->ioaddr = |
63 | ioremap(offset: xpcs_regs->addr, size: xpcs_regs->size); |
64 | if (!xpcs_regs->ioaddr) |
65 | return -ENOMEM; |
66 | } |
67 | |
68 | /* reset the PHY through MDIO*/ |
69 | pcs_value = xmdio_read(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_PCS, MDIO_CTRL1); |
70 | pcs_value |= MDIO_CTRL1_RESET; |
71 | xmdio_write(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_PCS, MDIO_CTRL1, value: pcs_value); |
72 | |
73 | count = 50; |
74 | do { |
75 | msleep(msecs: 20); |
76 | pcs_value = xmdio_read(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_PCS, |
77 | MDIO_CTRL1); |
78 | } while ((pcs_value & MDIO_CTRL1_RESET) && --count); |
79 | |
80 | if (pcs_value & MDIO_CTRL1_RESET) |
81 | dev_warn(vdev->device, "%s: XGBE PHY reset timeout\n" , |
82 | __func__); |
83 | |
84 | /* disable auto-negotiation */ |
85 | value = xmdio_read(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_CTRL1); |
86 | value &= ~MDIO_AN_CTRL1_ENABLE; |
87 | xmdio_write(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_CTRL1, value); |
88 | |
89 | /* disable AN IRQ */ |
90 | xmdio_write(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_AN_INTMASK, value: 0); |
91 | |
92 | /* clear AN IRQ */ |
93 | xmdio_write(ioaddr: xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_AN_INT, value: 0); |
94 | |
95 | /* MAC software reset */ |
96 | dma_mr_value = ioread32(xgmac_regs->ioaddr + DMA_MR); |
97 | dma_mr_value |= 0x1; |
98 | iowrite32(dma_mr_value, xgmac_regs->ioaddr + DMA_MR); |
99 | |
100 | usleep_range(min: 10, max: 15); |
101 | |
102 | count = 2000; |
103 | while (--count && (ioread32(xgmac_regs->ioaddr + DMA_MR) & 1)) |
104 | usleep_range(min: 500, max: 600); |
105 | |
106 | if (!count) |
107 | dev_warn(vdev->device, "%s: MAC SW reset failed\n" , __func__); |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | module_vfio_reset_handler("amd,xgbe-seattle-v1a" , vfio_platform_amdxgbe_reset); |
113 | |
114 | MODULE_VERSION("0.1" ); |
115 | MODULE_LICENSE("GPL v2" ); |
116 | MODULE_AUTHOR("Eric Auger <eric.auger@linaro.org>" ); |
117 | MODULE_DESCRIPTION("Reset support for AMD xgbe vfio platform device" ); |
118 | |