GNU Linux-libre 6.9.2-gnu
[releases.git] / include / linux / apple-gmux.h
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * apple-gmux.h - microcontroller built into dual GPU MacBook Pro & Mac Pro
4  * Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
5  */
6
7 #ifndef LINUX_APPLE_GMUX_H
8 #define LINUX_APPLE_GMUX_H
9
10 #include <linux/acpi.h>
11 #include <linux/io.h>
12 #include <linux/pnp.h>
13
14 #define GMUX_ACPI_HID "APP000B"
15
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
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
43 #define GMUX_MIN_IO_LEN                 (GMUX_PORT_BRIGHTNESS + 4)
44
45 enum apple_gmux_type {
46         APPLE_GMUX_TYPE_PIO,
47         APPLE_GMUX_TYPE_INDEXED,
48         APPLE_GMUX_TYPE_MMIO,
49 };
50
51 #if IS_ENABLED(CONFIG_APPLE_GMUX)
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 }
66
67 static inline bool apple_gmux_is_mmio(unsigned long iostart)
68 {
69         u8 __iomem *iomem_base = ioremap(iostart, 16);
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
85 /**
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
89  * @type_ret: Returns (by reference) the apple_gmux_type of the device
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  */
98 static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_type *type_ret)
99 {
100         u8 ver_major, ver_minor, ver_release;
101         struct device *dev = NULL;
102         struct acpi_device *adev;
103         struct resource *res;
104         enum apple_gmux_type type = APPLE_GMUX_TYPE_PIO;
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);
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;
139                 else
140                         goto out;
141         }
142
143         if (type_ret)
144                 *type_ret = type;
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
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  *
158  * Return: %true if gmux ACPI device is present and the kernel was configured
159  * with CONFIG_APPLE_GMUX, %false otherwise.
160  */
161 static inline bool apple_gmux_present(void)
162 {
163         return acpi_dev_found(GMUX_ACPI_HID);
164 }
165
166 #else  /* !CONFIG_APPLE_GMUX */
167
168 static inline bool apple_gmux_present(void)
169 {
170         return false;
171 }
172
173 static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
174 {
175         return false;
176 }
177
178 #endif /* !CONFIG_APPLE_GMUX */
179
180 #endif /* LINUX_APPLE_GMUX_H */