Commit | Line | Data |
---|---|---|
caab277b | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2413306c LW |
2 | /* |
3 | * apple-gmux.h - microcontroller built into dual GPU MacBook Pro & Mac Pro | |
4 | * Copyright (C) 2015 Lukas Wunner <lukas@wunner.de> | |
2413306c LW |
5 | */ |
6 | ||
7 | #ifndef LINUX_APPLE_GMUX_H | |
8 | #define LINUX_APPLE_GMUX_H | |
9 | ||
10 | #include <linux/acpi.h> | |
d143908f HG |
11 | #include <linux/io.h> |
12 | #include <linux/pnp.h> | |
2413306c LW |
13 | |
14 | #define GMUX_ACPI_HID "APP000B" | |
15 | ||
39f5a81f HG |
16 | /* |
17 | * gmux port offsets. Many of these are not yet used, but may be in the | |
18 | * future, and it's useful to have them documented here anyhow. | |
19 | */ | |
20 | #define GMUX_PORT_VERSION_MAJOR 0x04 | |
21 | #define GMUX_PORT_VERSION_MINOR 0x05 | |
22 | #define GMUX_PORT_VERSION_RELEASE 0x06 | |
23 | #define GMUX_PORT_SWITCH_DISPLAY 0x10 | |
24 | #define GMUX_PORT_SWITCH_GET_DISPLAY 0x11 | |
25 | #define GMUX_PORT_INTERRUPT_ENABLE 0x14 | |
26 | #define GMUX_PORT_INTERRUPT_STATUS 0x16 | |
27 | #define GMUX_PORT_SWITCH_DDC 0x28 | |
28 | #define GMUX_PORT_SWITCH_EXTERNAL 0x40 | |
29 | #define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41 | |
30 | #define GMUX_PORT_DISCRETE_POWER 0x50 | |
31 | #define GMUX_PORT_MAX_BRIGHTNESS 0x70 | |
32 | #define GMUX_PORT_BRIGHTNESS 0x74 | |
33 | #define GMUX_PORT_VALUE 0xc2 | |
34 | #define GMUX_PORT_READ 0xd0 | |
35 | #define GMUX_PORT_WRITE 0xd4 | |
36 | ||
0c18184d OC |
37 | #define GMUX_MMIO_PORT_SELECT 0x0e |
38 | #define GMUX_MMIO_COMMAND_SEND 0x0f | |
39 | ||
40 | #define GMUX_MMIO_READ 0x00 | |
41 | #define GMUX_MMIO_WRITE 0x40 | |
42 | ||
39f5a81f HG |
43 | #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) |
44 | ||
96ec2d98 OC |
45 | enum apple_gmux_type { |
46 | APPLE_GMUX_TYPE_PIO, | |
47 | APPLE_GMUX_TYPE_INDEXED, | |
0c18184d | 48 | APPLE_GMUX_TYPE_MMIO, |
96ec2d98 OC |
49 | }; |
50 | ||
b7172119 | 51 | #if IS_ENABLED(CONFIG_APPLE_GMUX) |
d143908f HG |
52 | static inline bool apple_gmux_is_indexed(unsigned long iostart) |
53 | { | |
54 | u16 val; | |
55 | ||
56 | outb(0xaa, iostart + 0xcc); | |
57 | outb(0x55, iostart + 0xcd); | |
58 | outb(0x00, iostart + 0xce); | |
59 | ||
60 | val = inb(iostart + 0xcc) | (inb(iostart + 0xcd) << 8); | |
61 | if (val == 0x55aa) | |
62 | return true; | |
63 | ||
64 | return false; | |
65 | } | |
b7172119 | 66 | |
0c18184d OC |
67 | static inline bool apple_gmux_is_mmio(unsigned long iostart) |
68 | { | |
fb131c47 | 69 | u8 __iomem *iomem_base = ioremap(iostart, 16); |
0c18184d OC |
70 | u8 val; |
71 | ||
72 | if (!iomem_base) | |
73 | return false; | |
74 | ||
75 | /* | |
76 | * If this is 0xff, then gmux must not be present, as the gmux would | |
77 | * reset it to 0x00, or it would be one of 0x1, 0x4, 0x41, 0x44 if a | |
78 | * command is currently being processed. | |
79 | */ | |
80 | val = ioread8(iomem_base + GMUX_MMIO_COMMAND_SEND); | |
81 | iounmap(iomem_base); | |
82 | return (val != 0xff); | |
83 | } | |
84 | ||
2413306c | 85 | /** |
d143908f HG |
86 | * apple_gmux_detect() - detect if gmux is built into the machine |
87 | * | |
88 | * @pnp_dev: Device to probe or NULL to use the first matching device | |
ad3d0ee8 | 89 | * @type_ret: Returns (by reference) the apple_gmux_type of the device |
d143908f HG |
90 | * |
91 | * Detect if a supported gmux device is present by actually probing it. | |
92 | * This avoids the false positives returned on some models by | |
93 | * apple_gmux_present(). | |
94 | * | |
95 | * Return: %true if a supported gmux ACPI device is detected and the kernel | |
96 | * was configured with CONFIG_APPLE_GMUX, %false otherwise. | |
97 | */ | |
96ec2d98 | 98 | static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_type *type_ret) |
d143908f HG |
99 | { |
100 | u8 ver_major, ver_minor, ver_release; | |
101 | struct device *dev = NULL; | |
102 | struct acpi_device *adev; | |
103 | struct resource *res; | |
96ec2d98 | 104 | enum apple_gmux_type type = APPLE_GMUX_TYPE_PIO; |
d143908f HG |
105 | bool ret = false; |
106 | ||
107 | if (!pnp_dev) { | |
108 | adev = acpi_dev_get_first_match_dev(GMUX_ACPI_HID, NULL, -1); | |
109 | if (!adev) | |
110 | return false; | |
111 | ||
112 | dev = get_device(acpi_get_first_physical_node(adev)); | |
113 | acpi_dev_put(adev); | |
114 | if (!dev) | |
115 | return false; | |
116 | ||
117 | pnp_dev = to_pnp_dev(dev); | |
118 | } | |
119 | ||
120 | res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0); | |
0c18184d OC |
121 | if (res && resource_size(res) >= GMUX_MIN_IO_LEN) { |
122 | /* | |
123 | * Invalid version information may indicate either that the gmux | |
124 | * device isn't present or that it's a new one that uses indexed io. | |
125 | */ | |
126 | ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR); | |
127 | ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR); | |
128 | ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE); | |
129 | if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { | |
130 | if (apple_gmux_is_indexed(res->start)) | |
131 | type = APPLE_GMUX_TYPE_INDEXED; | |
132 | else | |
133 | goto out; | |
134 | } | |
135 | } else { | |
136 | res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0); | |
137 | if (res && apple_gmux_is_mmio(res->start)) | |
138 | type = APPLE_GMUX_TYPE_MMIO; | |
96ec2d98 | 139 | else |
d143908f HG |
140 | goto out; |
141 | } | |
142 | ||
96ec2d98 OC |
143 | if (type_ret) |
144 | *type_ret = type; | |
d143908f HG |
145 | |
146 | ret = true; | |
147 | out: | |
148 | put_device(dev); | |
149 | return ret; | |
150 | } | |
151 | ||
152 | /** | |
153 | * apple_gmux_present() - check if gmux ACPI device is present | |
2413306c LW |
154 | * |
155 | * Drivers may use this to activate quirks specific to dual GPU MacBook Pros | |
156 | * and Mac Pros, e.g. for deferred probing, runtime pm and backlight. | |
157 | * | |
d143908f | 158 | * Return: %true if gmux ACPI device is present and the kernel was configured |
2413306c LW |
159 | * with CONFIG_APPLE_GMUX, %false otherwise. |
160 | */ | |
161 | static inline bool apple_gmux_present(void) | |
162 | { | |
c68ae33e | 163 | return acpi_dev_found(GMUX_ACPI_HID); |
2413306c LW |
164 | } |
165 | ||
b7172119 LW |
166 | #else /* !CONFIG_APPLE_GMUX */ |
167 | ||
168 | static inline bool apple_gmux_present(void) | |
169 | { | |
170 | return false; | |
171 | } | |
172 | ||
d143908f HG |
173 | static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret) |
174 | { | |
175 | return false; | |
176 | } | |
177 | ||
b7172119 LW |
178 | #endif /* !CONFIG_APPLE_GMUX */ |
179 | ||
2413306c | 180 | #endif /* LINUX_APPLE_GMUX_H */ |