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 */ 181
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.