Skip to content

Commit 0c18184

Browse files
Redecoratingjwrdegoede
authored andcommitted
platform/x86: apple-gmux: support MMIO gmux on T2 Macs
In some newer dual gpu MacBooks, the T2 Coprocessor functions as the gmux, and the Intel side can interract with this new gmux type through MMIO. Add support for these gmux controllers to the apple-gmux driver. We start using the GMSP(0) acpi method on these gmux's when clearing interrupts, as this prevents a flood of status=0 interrupts that can't be cleared. It's unknown if this helps or hinders older gmux types, so it isn't enabled for those. Interestingly, the ACPI table only allocates 8 bytes for GMUX, but we actually need 16, and as such we request 16 with request_mem_region. Reading and writing from ports: 16 bytes from 0xfe0b0200 are used. 0x0 to 0x4 are where data to read appears, and where data to write goes. Writing to 0xe sets the gmux port being accessed, and writing to 0xf sends commands. These commands are 0x40 & data_length for write, and data_length for read, where data_length is 1, 2 or 4. Once byte base+0xf is 0, the command is done. Issues: As with other retina models, we can't switch DDC lines so switching at runtime doesn't work if the inactive gpu driver already disabled eDP due to it not being connected when that driver loaded. Additionally, turning on the dgpu back on on the MacBookPro16,1 does not work. Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Orlando Chamberlain <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Hans de Goede <[email protected]>
1 parent 96ec2d9 commit 0c18184

File tree

2 files changed

+200
-26
lines changed

2 files changed

+200
-26
lines changed

drivers/platform/x86/apple-gmux.c

Lines changed: 157 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,35 @@
2828
* DOC: Overview
2929
*
3030
* gmux is a microcontroller built into the MacBook Pro to support dual GPUs:
31-
* A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on retinas.
31+
* A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on pre-T2 retinas.
32+
*
33+
* On T2 Macbooks, the gmux is part of the T2 Coprocessor's SMC. The SMC has
34+
* an I2C connection to a `NXP PCAL6524` GPIO expander, which enables/disables
35+
* the voltage regulators of the discrete GPU, drives the display panel power,
36+
* and has a GPIO to switch the eDP mux. The Intel CPU can interact with
37+
* gmux through MMIO, similar to how the main SMC interface is controlled.
3238
*
3339
* (The MacPro6,1 2013 also has a gmux, however it is unclear why since it has
3440
* dual GPUs but no built-in display.)
3541
*
3642
* gmux is connected to the LPC bus of the southbridge. Its I/O ports are
3743
* accessed differently depending on the microcontroller: Driver functions
38-
* to access a pre-retina gmux are infixed ``_pio_``, those for a retina gmux
39-
* are infixed ``_index_``.
44+
* to access a pre-retina gmux are infixed ``_pio_``, those for a pre-T2
45+
* retina gmux are infixed ``_index_``, and those on T2 Macs are infixed
46+
* with ``_mmio_``.
4047
*
4148
* .. _Lattice XP2:
4249
* http://www.latticesemi.com/en/Products/FPGAandCPLD/LatticeXP2.aspx
4350
* .. _Renesas R4F2113:
4451
* http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp
52+
* .. _NXP PCAL6524:
53+
* https://www.nxp.com/docs/en/data-sheet/PCAL6524.pdf
4554
*/
4655

4756
struct apple_gmux_config;
4857

4958
struct apple_gmux_data {
59+
u8 *__iomem iomem_base;
5060
unsigned long iostart;
5161
unsigned long iolen;
5262
const struct apple_gmux_config *config;
@@ -208,6 +218,79 @@ static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
208218
mutex_unlock(&gmux_data->index_lock);
209219
}
210220

221+
static int gmux_mmio_wait(struct apple_gmux_data *gmux_data)
222+
{
223+
int i = 200;
224+
u8 gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
225+
226+
while (i && gwr) {
227+
gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
228+
udelay(100);
229+
i--;
230+
}
231+
232+
return !!i;
233+
}
234+
235+
static u8 gmux_mmio_read8(struct apple_gmux_data *gmux_data, int port)
236+
{
237+
u8 val;
238+
239+
mutex_lock(&gmux_data->index_lock);
240+
gmux_mmio_wait(gmux_data);
241+
iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
242+
iowrite8(GMUX_MMIO_READ | sizeof(val),
243+
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
244+
gmux_mmio_wait(gmux_data);
245+
val = ioread8(gmux_data->iomem_base);
246+
mutex_unlock(&gmux_data->index_lock);
247+
248+
return val;
249+
}
250+
251+
static void gmux_mmio_write8(struct apple_gmux_data *gmux_data, int port,
252+
u8 val)
253+
{
254+
mutex_lock(&gmux_data->index_lock);
255+
gmux_mmio_wait(gmux_data);
256+
iowrite8(val, gmux_data->iomem_base);
257+
258+
iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
259+
iowrite8(GMUX_MMIO_WRITE | sizeof(val),
260+
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
261+
262+
gmux_mmio_wait(gmux_data);
263+
mutex_unlock(&gmux_data->index_lock);
264+
}
265+
266+
static u32 gmux_mmio_read32(struct apple_gmux_data *gmux_data, int port)
267+
{
268+
u32 val;
269+
270+
mutex_lock(&gmux_data->index_lock);
271+
gmux_mmio_wait(gmux_data);
272+
iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
273+
iowrite8(GMUX_MMIO_READ | sizeof(val),
274+
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
275+
gmux_mmio_wait(gmux_data);
276+
val = be32_to_cpu(ioread32(gmux_data->iomem_base));
277+
mutex_unlock(&gmux_data->index_lock);
278+
279+
return val;
280+
}
281+
282+
static void gmux_mmio_write32(struct apple_gmux_data *gmux_data, int port,
283+
u32 val)
284+
{
285+
mutex_lock(&gmux_data->index_lock);
286+
iowrite32(cpu_to_be32(val), gmux_data->iomem_base);
287+
iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
288+
iowrite8(GMUX_MMIO_WRITE | sizeof(val),
289+
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
290+
gmux_mmio_wait(gmux_data);
291+
mutex_unlock(&gmux_data->index_lock);
292+
}
293+
211294
static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
212295
{
213296
return gmux_data->config->read8(gmux_data, port);
@@ -236,8 +319,8 @@ static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
236319
* the GPU. On dual GPU MacBook Pros by contrast, either GPU may be suspended
237320
* to conserve energy. Hence the PWM signal needs to be generated by a separate
238321
* backlight driver which is controlled by gmux. The earliest generation
239-
* MBP5 2008/09 uses a `TI LP8543`_ backlight driver. All newer models
240-
* use a `TI LP8545`_.
322+
* MBP5 2008/09 uses a `TI LP8543`_ backlight driver. Newer models
323+
* use a `TI LP8545`_ or a TI LP8548.
241324
*
242325
* .. _TI LP8543: https://www.ti.com/lit/ds/symlink/lp8543.pdf
243326
* .. _TI LP8545: https://www.ti.com/lit/ds/symlink/lp8545.pdf
@@ -301,8 +384,8 @@ static const struct backlight_ops gmux_bl_ops = {
301384
* connecting it either to the discrete GPU or the Thunderbolt controller.
302385
* Oddly enough, while the full port is no longer switchable, AUX and HPD
303386
* are still switchable by way of an `NXP CBTL03062`_ (on pre-retinas
304-
* MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on retinas) under the
305-
* control of gmux. Since the integrated GPU is missing the main link,
387+
* MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on pre-t2 retinas) under
388+
* the control of gmux. Since the integrated GPU is missing the main link,
306389
* external displays appear to it as phantoms which fail to link-train.
307390
*
308391
* gmux receives the HPD signal of all display connectors and sends an
@@ -503,14 +586,42 @@ static const struct apple_gmux_config apple_gmux_index = {
503586
.name = "indexed"
504587
};
505588

589+
static const struct apple_gmux_config apple_gmux_mmio = {
590+
.read8 = &gmux_mmio_read8,
591+
.write8 = &gmux_mmio_write8,
592+
.read32 = &gmux_mmio_read32,
593+
.write32 = &gmux_mmio_write32,
594+
.gmux_handler = &gmux_handler_no_ddc,
595+
.handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG,
596+
.resource_type = IORESOURCE_MEM,
597+
.read_version_as_u32 = true,
598+
.name = "T2"
599+
};
600+
601+
506602
/**
507603
* DOC: Interrupt
508604
*
509605
* gmux is also connected to a GPIO pin of the southbridge and thereby is able
510-
* to trigger an ACPI GPE. On the MBP5 2008/09 it's GPIO pin 22 of the Nvidia
511-
* MCP79, on all following generations it's GPIO pin 6 of the Intel PCH.
606+
* to trigger an ACPI GPE. ACPI name GMGP holds this GPIO pin's number. On the
607+
* MBP5 2008/09 it's GPIO pin 22 of the Nvidia MCP79, on following generations
608+
* it's GPIO pin 6 of the Intel PCH, on MMIO gmux's it's pin 21.
609+
*
512610
* The GPE merely signals that an interrupt occurred, the actual type of event
513611
* is identified by reading a gmux register.
612+
*
613+
* In addition to the GMGP name, gmux's ACPI device also has two methods GMSP
614+
* and GMLV. GMLV likely means "GMUX Level", and reads the value of the GPIO,
615+
* while GMSP likely means "GMUX Set Polarity", and seems to write to the GPIO's
616+
* value. On newer Macbooks (This was introduced with or sometime before the
617+
* MacBookPro14,3), the ACPI GPE method differentiates between the OS type: On
618+
* Darwin, only a notification is signaled, whereas on other OSes, the GPIO's
619+
* value is read and then inverted.
620+
*
621+
* Because Linux masquerades as Darwin, it ends up in the notification-only code
622+
* path. On MMIO gmux's, this seems to lead to us being unable to clear interrupts,
623+
* unless we call GMSP(0). Without this, there is a flood of status=0 interrupts
624+
* that can't be cleared. This issue seems to be unique to MMIO gmux's.
514625
*/
515626

516627
static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
@@ -537,6 +648,9 @@ static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
537648
/* to clear interrupts write back current status */
538649
status = gmux_interrupt_get_status(gmux_data);
539650
gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
651+
/* Prevent flood of status=0 interrupts */
652+
if (gmux_data->config == &apple_gmux_mmio)
653+
acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0);
540654
}
541655

542656
static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
@@ -609,6 +723,25 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
609723
pnp_set_drvdata(pnp, gmux_data);
610724

611725
switch (type) {
726+
case APPLE_GMUX_TYPE_MMIO:
727+
gmux_data->config = &apple_gmux_mmio;
728+
mutex_init(&gmux_data->index_lock);
729+
730+
res = pnp_get_resource(pnp, IORESOURCE_MEM, 0);
731+
gmux_data->iostart = res->start;
732+
/* Although the ACPI table only allocates 8 bytes, we need 16. */
733+
gmux_data->iolen = 16;
734+
if (!request_mem_region(gmux_data->iostart, gmux_data->iolen,
735+
"Apple gmux")) {
736+
pr_err("gmux I/O already in use\n");
737+
goto err_free;
738+
}
739+
gmux_data->iomem_base = ioremap(gmux_data->iostart, gmux_data->iolen);
740+
if (!gmux_data->iomem_base) {
741+
pr_err("couldn't remap gmux mmio region");
742+
goto err_release;
743+
}
744+
goto get_version;
612745
case APPLE_GMUX_TYPE_INDEXED:
613746
gmux_data->config = &apple_gmux_index;
614747
mutex_init(&gmux_data->index_lock);
@@ -628,6 +761,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
628761
goto err_free;
629762
}
630763

764+
get_version:
631765
if (gmux_data->config->read_version_as_u32) {
632766
version = gmux_read32(gmux_data, GMUX_PORT_VERSION_MAJOR);
633767
ver_major = (version >> 24) & 0xff;
@@ -658,7 +792,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
658792
gmux_data, &gmux_bl_ops, &props);
659793
if (IS_ERR(bdev)) {
660794
ret = PTR_ERR(bdev);
661-
goto err_release;
795+
goto err_unmap;
662796
}
663797

664798
gmux_data->bdev = bdev;
@@ -725,7 +859,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
725859
/*
726860
* Retina MacBook Pros cannot switch the panel's AUX separately
727861
* and need eDP pre-calibration. They are distinguishable from
728-
* pre-retinas by having an "indexed" gmux.
862+
* pre-retinas by having an "indexed" or "T2" gmux.
729863
*
730864
* Pre-retina MacBook Pros can switch the panel's DDC separately.
731865
*/
@@ -750,8 +884,14 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
750884
&gmux_notify_handler);
751885
err_notify:
752886
backlight_device_unregister(bdev);
887+
err_unmap:
888+
if (gmux_data->iomem_base)
889+
iounmap(gmux_data->iomem_base);
753890
err_release:
754-
release_region(gmux_data->iostart, gmux_data->iolen);
891+
if (gmux_data->config->resource_type == IORESOURCE_MEM)
892+
release_mem_region(gmux_data->iostart, gmux_data->iolen);
893+
else
894+
release_region(gmux_data->iostart, gmux_data->iolen);
755895
err_free:
756896
kfree(gmux_data);
757897
return ret;
@@ -772,7 +912,11 @@ static void gmux_remove(struct pnp_dev *pnp)
772912

773913
backlight_device_unregister(gmux_data->bdev);
774914

775-
release_region(gmux_data->iostart, gmux_data->iolen);
915+
if (gmux_data->iomem_base) {
916+
iounmap(gmux_data->iomem_base);
917+
release_mem_region(gmux_data->iostart, gmux_data->iolen);
918+
} else
919+
release_region(gmux_data->iostart, gmux_data->iolen);
776920
apple_gmux_data = NULL;
777921
kfree(gmux_data);
778922

include/linux/apple-gmux.h

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,18 @@
3434
#define GMUX_PORT_READ 0xd0
3535
#define GMUX_PORT_WRITE 0xd4
3636

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+
3743
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
3844

3945
enum apple_gmux_type {
4046
APPLE_GMUX_TYPE_PIO,
4147
APPLE_GMUX_TYPE_INDEXED,
48+
APPLE_GMUX_TYPE_MMIO,
4249
};
4350

4451
#if IS_ENABLED(CONFIG_APPLE_GMUX)
@@ -57,6 +64,24 @@ static inline bool apple_gmux_is_indexed(unsigned long iostart)
5764
return false;
5865
}
5966

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+
6085
/**
6186
* apple_gmux_detect() - detect if gmux is built into the machine
6287
*
@@ -93,19 +118,24 @@ static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_ty
93118
}
94119

95120
res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0);
96-
if (!res || resource_size(res) < GMUX_MIN_IO_LEN)
97-
goto out;
98-
99-
/*
100-
* Invalid version information may indicate either that the gmux
101-
* device isn't present or that it's a new one that uses indexed io.
102-
*/
103-
ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR);
104-
ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR);
105-
ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE);
106-
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
107-
if (apple_gmux_is_indexed(res->start))
108-
type = APPLE_GMUX_TYPE_INDEXED;
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;
109139
else
110140
goto out;
111141
}

0 commit comments

Comments
 (0)