Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions Documentation/devicetree/bindings/gpio/apple,smc-pcio-gpio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0-only
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/apple,smc-pcio-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Apple SMC pcIO output GPIOs

maintainers:
- Dj <hey@djdev.me>

description: |
The writable eight-byte pcIO SMC key controls outputs through board
command templates. Each template has its low 16 bits clear; the driver
inserts a boolean output level and sends the little-endian command.
Input reads and mode changes are not supported by this interface.

properties:
compatible:
const: apple,smc-pcio-gpio

gpio-controller: true

'#gpio-cells':
const: 2

apple,output-commands:
$ref: /schemas/types.yaml#/definitions/uint64-array
minItems: 1
maxItems: 64
uniqueItems: true
description: Unique command templates with low 16 bits clear, in GPIO order

required:
- compatible
- gpio-controller
- '#gpio-cells'
- apple,output-commands

additionalProperties: false
7 changes: 5 additions & 2 deletions Documentation/devicetree/bindings/mfd/apple,smc.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) AND GPL-2.0-only
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/apple,smc.yaml#
Expand All @@ -16,6 +16,7 @@ description:
properties:
compatible:
oneOf:
- const: apple,t6050-smc
- items:
- enum:
- apple,t6020-smc
Expand Down Expand Up @@ -45,7 +46,9 @@ properties:
maxItems: 1

gpio:
$ref: /schemas/gpio/apple,smc-gpio.yaml
oneOf:
- $ref: /schemas/gpio/apple,smc-gpio.yaml
- $ref: /schemas/gpio/apple,smc-pcio-gpio.yaml

reboot:
$ref: /schemas/power/reset/apple,smc-reboot.yaml
Expand Down
7 changes: 7 additions & 0 deletions arch/arm64/configs/aurora-apif.config
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ CONFIG_NVMEM=y
CONFIG_NVMEM_APPLE_SPMI=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_APPLE_SPMI_PMU=y
CONFIG_MFD_MACSMC=y
CONFIG_GPIO_MACSMC=y
CONFIG_INPUT=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_MACSMC_INPUT=y
CONFIG_HWMON=y
CONFIG_SENSORS_MACSMC_HWMON=y
72 changes: 71 additions & 1 deletion drivers/gpio/gpio-macsmc.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// SPDX-License-Identifier: (GPL-2.0-only OR MIT) AND GPL-2.0-only
/*
* Apple SMC GPIO driver
* Copyright The Asahi Linux Contributors
Expand All @@ -14,6 +14,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/macsmc.h>
#include <linux/of.h>
#include <linux/unaligned.h>

#define MAX_GPIO 64

Expand Down Expand Up @@ -91,6 +92,7 @@ struct macsmc_gpio {
struct apple_smc *smc;
struct gpio_chip gc;

u64 *output_commands;
int first_index;
smc_key base_key;

Expand Down Expand Up @@ -296,6 +298,71 @@ static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc,
return 0;
}

/* Each DT template identifies one output; its low 16 bits carry the value. */
static int macsmc_pcio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
u8 command[8];
int ret;

put_unaligned_le64(smcgp->output_commands[offset] | !!value, command);
ret = apple_smc_write(smcgp->smc, SMC_KEY(pcIO), command, sizeof(command));
return ret < 0 ? ret : ret == sizeof(command) ? 0 : -EIO;
}

static int macsmc_pcio_get(struct gpio_chip *gc, unsigned int offset)
{
/* The native output command has no established input/readback operation. */
return -EOPNOTSUPP;
}

static int macsmc_pcio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}

static int macsmc_pcio_probe(struct macsmc_gpio *smcgp)
{
struct device *dev = smcgp->dev;
struct apple_smc_key_info info;
int count, ret;

ret = apple_smc_get_key_info(smcgp->smc, SMC_KEY(pcIO), &info);
if (ret < 0)
return ret;
if (info.size != 8 || !(info.flags & APPLE_SMC_WRITABLE))
return dev_err_probe(dev, -EINVAL, "pcIO is not an eight-byte output key\n");
count = of_property_count_u64_elems(dev->of_node, "apple,output-commands");
if (count <= 0 || count > MAX_GPIO)
return -EINVAL;
smcgp->output_commands = devm_kcalloc(dev, count, sizeof(u64), GFP_KERNEL);
if (!smcgp->output_commands)
return -ENOMEM;
ret = of_property_read_u64_array(dev->of_node, "apple,output-commands",
smcgp->output_commands, count);
if (ret)
return ret;
for (int i = 0; i < count; i++) {
if (smcgp->output_commands[i] & 0xffff)
return -EINVAL;
for (int j = 0; j < i; j++)
if (smcgp->output_commands[i] == smcgp->output_commands[j])
return -EINVAL;
}

smcgp->gc.label = "macsmc-pcio-output";
smcgp->gc.owner = THIS_MODULE;
smcgp->gc.get = macsmc_pcio_get;
smcgp->gc.set = macsmc_pcio_set;
smcgp->gc.direction_output = macsmc_pcio_set;
smcgp->gc.get_direction = macsmc_pcio_get_direction;
smcgp->gc.can_sleep = true;
smcgp->gc.ngpio = count;
smcgp->gc.base = -1;
smcgp->gc.parent = dev;
return devm_gpiochip_add_data(dev, &smcgp->gc, smcgp);
}

struct macsmc_gpio_of_match_data {
smc_key base_key;
};
Expand All @@ -314,6 +381,8 @@ static int macsmc_gpio_probe(struct platform_device *pdev)

smcgp->dev = &pdev->dev;
smcgp->smc = smc;
if (device_is_compatible(&pdev->dev, "apple,smc-pcio-gpio"))
return macsmc_pcio_probe(smcgp);
smcgp->base_key = data ? data->base_key : _SMC_KEY("gP\0\0");
smcgp->j700_pcio = of_machine_is_compatible("apple,j700") &&
device_is_compatible(&pdev->dev, "apple,smc-gpio");
Expand Down Expand Up @@ -357,6 +426,7 @@ static const struct macsmc_gpio_of_match_data macsmc_gpio_low_data = {
};

static const struct of_device_id macsmc_gpio_of_table[] = {
{ .compatible = "apple,smc-pcio-gpio" },
{
.compatible = "apple,smc-gpio",
.data = &macsmc_gpio_up_data,
Expand Down
32 changes: 28 additions & 4 deletions drivers/mfd/macsmc.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// SPDX-License-Identifier: (GPL-2.0-only OR MIT) AND GPL-2.0-only
/*
* Apple SMC (System Management Controller) MFD driver
*
Expand Down Expand Up @@ -53,6 +53,15 @@ static const struct mfd_cell apple_smc_devs[] = {
MFD_CELL_OF("macsmc-rtc", NULL, NULL, 0, 0, "apple,smc-rtc"),
};

/* This profile exposes notifications and the explicitly described outputs. */
static const struct mfd_cell apple_smc_t6050_devs[] = {
MFD_CELL_NAME("macsmc-input"),
MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"),
};

static const struct mfd_cell apple_smc_pcio_dev =
MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-pcio-gpio");

/* The second GPIO bank ('gp' keys) only exists on some SMCs. */
static const struct mfd_cell apple_smc_low_gpio_dev =
MFD_CELL_OF("macsmc-low-gpio", NULL, NULL, 0, 0, "apple,smc-low-gpio");
Expand Down Expand Up @@ -479,12 +488,26 @@ static int apple_smc_probe(struct platform_device *pdev)
if (ret)
return ret;

ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
apple_smc_devs, ARRAY_SIZE(apple_smc_devs),
NULL, 0, NULL);
if (of_device_is_compatible(dev->of_node, "apple,t6050-smc"))
ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
apple_smc_t6050_devs,
ARRAY_SIZE(apple_smc_t6050_devs), NULL, 0, NULL);
else
ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
apple_smc_devs, ARRAY_SIZE(apple_smc_devs),
NULL, 0, NULL);
if (ret)
return dev_err_probe(smc->dev, ret, "Failed to register sub-devices");

np = of_get_compatible_child(dev->of_node, "apple,smc-pcio-gpio");
if (np) {
of_node_put(np);
ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
&apple_smc_pcio_dev, 1, NULL, 0, NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to register pcIO outputs\n");
}

/*
* Register the second GPIO bank only where the device tree describes
* it: the MFD core would otherwise create an unbound device and warn
Expand All @@ -504,6 +527,7 @@ static int apple_smc_probe(struct platform_device *pdev)
}

static const struct of_device_id apple_smc_of_match[] = {
{ .compatible = "apple,t6050-smc" },
{ .compatible = "apple,t8103-smc" },
{ .compatible = "apple,smc" },
{},
Expand Down