mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-15 17:51:41 -04:00
Merge branches 'ib-mfd-gpio-input-pwm-6.17', 'ib-mfd-gpio-power-soc-6.17' and 'ib-mfd-misc-pinctrl-6.17' into ibs-for-mfd-merged
This commit is contained in:
29
Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml
Normal file
29
Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/apple,smc-gpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple Mac System Management Controller GPIO
|
||||
|
||||
maintainers:
|
||||
- Sven Peter <sven@kernel.org>
|
||||
|
||||
description:
|
||||
Apple Mac System Management Controller GPIO block.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: apple,smc-gpio
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
|
||||
additionalProperties: false
|
||||
79
Documentation/devicetree/bindings/mfd/apple,smc.yaml
Normal file
79
Documentation/devicetree/bindings/mfd/apple,smc.yaml
Normal file
@@ -0,0 +1,79 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/apple,smc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple Mac System Management Controller
|
||||
|
||||
maintainers:
|
||||
- Sven Peter <sven@kernel.org>
|
||||
|
||||
description:
|
||||
Apple Mac System Management Controller implements various functions
|
||||
such as GPIO, RTC, power, reboot.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t6000-smc
|
||||
- apple,t8103-smc
|
||||
- apple,t8112-smc
|
||||
- const: apple,smc
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: SMC area
|
||||
- description: SRAM area
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: smc
|
||||
- const: sram
|
||||
|
||||
mboxes:
|
||||
maxItems: 1
|
||||
|
||||
gpio:
|
||||
$ref: /schemas/gpio/apple,smc-gpio.yaml
|
||||
|
||||
reboot:
|
||||
$ref: /schemas/power/reset/apple,smc-reboot.yaml
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- mboxes
|
||||
|
||||
examples:
|
||||
- |
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
smc@23e400000 {
|
||||
compatible = "apple,t8103-smc", "apple,smc";
|
||||
reg = <0x2 0x3e400000 0x0 0x4000>,
|
||||
<0x2 0x3fe00000 0x0 0x100000>;
|
||||
reg-names = "smc", "sram";
|
||||
mboxes = <&smc_mbox>;
|
||||
|
||||
smc_gpio: gpio {
|
||||
compatible = "apple,smc-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
||||
reboot {
|
||||
compatible = "apple,smc-reboot";
|
||||
nvmem-cells = <&shutdown_flag>, <&boot_stage>,
|
||||
<&boot_error_count>, <&panic_count>;
|
||||
nvmem-cell-names = "shutdown_flag", "boot_stage",
|
||||
"boot_error_count", "panic_count";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- ti,tps6593-q1
|
||||
- ti,tps6594-q1
|
||||
- ti,tps65224-q1
|
||||
- ti,tps652g1
|
||||
|
||||
reg:
|
||||
description: I2C slave address or SPI chip select number.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/reset/apple,smc-reboot.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple SMC Reboot Controller
|
||||
|
||||
description:
|
||||
The Apple System Management Controller (SMC) provides reboot functionality
|
||||
on Apple Silicon SoCs. It uses NVMEM cells to store and track various
|
||||
system state information related to boot, shutdown, and panic events.
|
||||
|
||||
maintainers:
|
||||
- Sven Peter <sven@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: apple,smc-reboot
|
||||
|
||||
nvmem-cells:
|
||||
items:
|
||||
- description: Flag indicating shutdown (as opposed to reboot)
|
||||
- description: Stage at which the boot process stopped (0x30 for normal boot)
|
||||
- description: Counter for boot errors
|
||||
- description: Counter for system panics
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: shutdown_flag
|
||||
- const: boot_stage
|
||||
- const: boot_error_count
|
||||
- const: panic_count
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- nvmem-cells
|
||||
- nvmem-cell-names
|
||||
|
||||
additionalProperties: false
|
||||
@@ -2332,6 +2332,7 @@ F: Documentation/devicetree/bindings/arm/apple/*
|
||||
F: Documentation/devicetree/bindings/clock/apple,nco.yaml
|
||||
F: Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml
|
||||
F: Documentation/devicetree/bindings/dma/apple,admac.yaml
|
||||
F: Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml
|
||||
F: Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/apple,*
|
||||
@@ -2339,6 +2340,7 @@ F: Documentation/devicetree/bindings/iommu/apple,dart.yaml
|
||||
F: Documentation/devicetree/bindings/iommu/apple,sart.yaml
|
||||
F: Documentation/devicetree/bindings/leds/backlight/apple,dwi-bl.yaml
|
||||
F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
|
||||
F: Documentation/devicetree/bindings/mfd/apple,smc.yaml
|
||||
F: Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml
|
||||
F: Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml
|
||||
F: Documentation/devicetree/bindings/nvmem/apple,efuses.yaml
|
||||
@@ -2346,6 +2348,7 @@ F: Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml
|
||||
F: Documentation/devicetree/bindings/pci/apple,pcie.yaml
|
||||
F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
|
||||
F: Documentation/devicetree/bindings/power/apple*
|
||||
F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml
|
||||
F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml
|
||||
F: Documentation/devicetree/bindings/spi/apple,spi.yaml
|
||||
F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml
|
||||
@@ -2355,6 +2358,7 @@ F: drivers/bluetooth/hci_bcm4377.c
|
||||
F: drivers/clk/clk-apple-nco.c
|
||||
F: drivers/cpufreq/apple-soc-cpufreq.c
|
||||
F: drivers/dma/apple-admac.c
|
||||
F: drivers/gpio/gpio-macsmc.c
|
||||
F: drivers/pmdomain/apple/
|
||||
F: drivers/i2c/busses/i2c-pasemi-core.c
|
||||
F: drivers/i2c/busses/i2c-pasemi-platform.c
|
||||
@@ -2362,10 +2366,12 @@ F: drivers/input/touchscreen/apple_z2.c
|
||||
F: drivers/iommu/apple-dart.c
|
||||
F: drivers/iommu/io-pgtable-dart.c
|
||||
F: drivers/irqchip/irq-apple-aic.c
|
||||
F: drivers/mfd/macsmc.c
|
||||
F: drivers/nvme/host/apple.c
|
||||
F: drivers/nvmem/apple-efuses.c
|
||||
F: drivers/nvmem/apple-spmi-nvmem.c
|
||||
F: drivers/pinctrl/pinctrl-apple-gpio.c
|
||||
F: drivers/power/reset/macsmc-reboot.c
|
||||
F: drivers/pwm/pwm-apple.c
|
||||
F: drivers/soc/apple/*
|
||||
F: drivers/spi/spi-apple.c
|
||||
@@ -2374,6 +2380,7 @@ F: drivers/video/backlight/apple_dwi_bl.c
|
||||
F: drivers/watchdog/apple_wdt.c
|
||||
F: include/dt-bindings/interrupt-controller/apple-aic.h
|
||||
F: include/dt-bindings/pinctrl/apple.h
|
||||
F: include/linux/mfd/macsmc.h
|
||||
F: include/linux/soc/apple/*
|
||||
F: include/uapi/drm/asahi_drm.h
|
||||
|
||||
|
||||
@@ -1465,6 +1465,16 @@ config GPIO_LP87565
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called gpio-lp87565.
|
||||
|
||||
config GPIO_MACSMC
|
||||
tristate "Apple Mac SMC GPIO"
|
||||
depends on MFD_MACSMC
|
||||
help
|
||||
Support for GPIOs controlled by the SMC microcontroller on Apple Mac
|
||||
systems.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called gpio-macsmc.
|
||||
|
||||
config GPIO_MADERA
|
||||
tristate "Cirrus Logic Madera class codecs"
|
||||
depends on PINCTRL_MADERA
|
||||
|
||||
@@ -99,6 +99,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
|
||||
obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o
|
||||
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
|
||||
obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o
|
||||
obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o
|
||||
obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o
|
||||
obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o
|
||||
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
|
||||
|
||||
292
drivers/gpio/gpio-macsmc.c
Normal file
292
drivers/gpio/gpio-macsmc.c
Normal file
@@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* Apple SMC GPIO driver
|
||||
* Copyright The Asahi Linux Contributors
|
||||
*
|
||||
* This driver implements basic SMC PMU GPIO support that can read inputs
|
||||
* and write outputs. Mode changes and IRQ config are not yet implemented.
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/macsmc.h>
|
||||
|
||||
#define MAX_GPIO 64
|
||||
|
||||
/*
|
||||
* Commands 0-6 are, presumably, the intended API.
|
||||
* Command 0xff lets you get/set the pin configuration in detail directly,
|
||||
* but the bit meanings seem not to be stable between devices/PMU hardware
|
||||
* versions.
|
||||
*
|
||||
* We're going to try to make do with the low commands for now.
|
||||
* We don't implement pin mode changes at this time.
|
||||
*/
|
||||
|
||||
#define CMD_ACTION (0 << 24)
|
||||
#define CMD_OUTPUT (1 << 24)
|
||||
#define CMD_INPUT (2 << 24)
|
||||
#define CMD_PINMODE (3 << 24)
|
||||
#define CMD_IRQ_ENABLE (4 << 24)
|
||||
#define CMD_IRQ_ACK (5 << 24)
|
||||
#define CMD_IRQ_MODE (6 << 24)
|
||||
#define CMD_CONFIG (0xff << 24)
|
||||
|
||||
#define MODE_INPUT 0
|
||||
#define MODE_OUTPUT 1
|
||||
#define MODE_VALUE_0 0
|
||||
#define MODE_VALUE_1 2
|
||||
|
||||
#define IRQ_MODE_HIGH 0
|
||||
#define IRQ_MODE_LOW 1
|
||||
#define IRQ_MODE_RISING 2
|
||||
#define IRQ_MODE_FALLING 3
|
||||
#define IRQ_MODE_BOTH 4
|
||||
|
||||
#define CONFIG_MASK GENMASK(23, 16)
|
||||
#define CONFIG_VAL GENMASK(7, 0)
|
||||
|
||||
#define CONFIG_OUTMODE GENMASK(7, 6)
|
||||
#define CONFIG_IRQMODE GENMASK(5, 3)
|
||||
#define CONFIG_PULLDOWN BIT(2)
|
||||
#define CONFIG_PULLUP BIT(1)
|
||||
#define CONFIG_OUTVAL BIT(0)
|
||||
|
||||
/*
|
||||
* Output modes seem to differ depending on the PMU in use... ?
|
||||
* j274 / M1 (Sera PMU):
|
||||
* 0 = input
|
||||
* 1 = output
|
||||
* 2 = open drain
|
||||
* 3 = disable
|
||||
* j314 / M1Pro (Maverick PMU):
|
||||
* 0 = input
|
||||
* 1 = open drain
|
||||
* 2 = output
|
||||
* 3 = ?
|
||||
*/
|
||||
|
||||
struct macsmc_gpio {
|
||||
struct device *dev;
|
||||
struct apple_smc *smc;
|
||||
struct gpio_chip gc;
|
||||
|
||||
int first_index;
|
||||
};
|
||||
|
||||
static int macsmc_gpio_nr(smc_key key)
|
||||
{
|
||||
int low = hex_to_bin(key & 0xff);
|
||||
int high = hex_to_bin((key >> 8) & 0xff);
|
||||
|
||||
if (low < 0 || high < 0)
|
||||
return -1;
|
||||
|
||||
return low | (high << 4);
|
||||
}
|
||||
|
||||
static int macsmc_gpio_key(unsigned int offset)
|
||||
{
|
||||
return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset);
|
||||
}
|
||||
|
||||
static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp)
|
||||
{
|
||||
struct apple_smc *smc = smcgp->smc;
|
||||
smc_key key = macsmc_gpio_key(0);
|
||||
smc_key first_key, last_key;
|
||||
int start, count, ret;
|
||||
|
||||
/* Return early if the key is out of bounds */
|
||||
ret = apple_smc_get_key_by_index(smc, 0, &first_key);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (key <= first_key)
|
||||
return -ENODEV;
|
||||
|
||||
ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (key > last_key)
|
||||
return -ENODEV;
|
||||
|
||||
/* Binary search to find index of first SMC key bigger or equal to key */
|
||||
start = 0;
|
||||
count = smc->key_count;
|
||||
while (count > 1) {
|
||||
smc_key pkey;
|
||||
int pivot = start + ((count - 1) >> 1);
|
||||
|
||||
ret = apple_smc_get_key_by_index(smc, pivot, &pkey);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (pkey == key)
|
||||
return pivot;
|
||||
|
||||
pivot++;
|
||||
|
||||
if (pkey < key) {
|
||||
count -= pivot - start;
|
||||
start = pivot;
|
||||
} else {
|
||||
count = pivot - start;
|
||||
}
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
|
||||
smc_key key = macsmc_gpio_key(offset);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* First try reading the explicit pin mode register */
|
||||
ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val);
|
||||
if (!ret)
|
||||
return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
/*
|
||||
* Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode.
|
||||
* Fall back to reading IRQ mode, which will only succeed for inputs.
|
||||
*/
|
||||
ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val);
|
||||
return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
|
||||
}
|
||||
|
||||
static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
|
||||
smc_key key = macsmc_gpio_key(offset);
|
||||
u32 cmd, val;
|
||||
int ret;
|
||||
|
||||
ret = macsmc_gpio_get_direction(gc, offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret == GPIO_LINE_DIRECTION_OUT)
|
||||
cmd = CMD_OUTPUT;
|
||||
else
|
||||
cmd = CMD_INPUT;
|
||||
|
||||
ret = apple_smc_rw_u32(smcgp->smc, key, cmd, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
|
||||
static int macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
{
|
||||
struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
|
||||
smc_key key = macsmc_gpio_key(offset);
|
||||
int ret;
|
||||
|
||||
value |= CMD_OUTPUT;
|
||||
ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value);
|
||||
if (ret < 0)
|
||||
dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n",
|
||||
&key, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc,
|
||||
unsigned long *valid_mask, unsigned int ngpios)
|
||||
{
|
||||
struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
|
||||
int count;
|
||||
int i;
|
||||
|
||||
count = min(smcgp->smc->key_count, MAX_GPIO);
|
||||
|
||||
bitmap_zero(valid_mask, ngpios);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
int ret, gpio_nr;
|
||||
smc_key key;
|
||||
|
||||
ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (key > SMC_KEY(gPff))
|
||||
break;
|
||||
|
||||
gpio_nr = macsmc_gpio_nr(key);
|
||||
if (gpio_nr < 0 || gpio_nr > MAX_GPIO) {
|
||||
dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key);
|
||||
continue;
|
||||
}
|
||||
|
||||
set_bit(gpio_nr, valid_mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsmc_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct macsmc_gpio *smcgp;
|
||||
struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
|
||||
smc_key key;
|
||||
int ret;
|
||||
|
||||
smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL);
|
||||
if (!smcgp)
|
||||
return -ENOMEM;
|
||||
|
||||
smcgp->dev = &pdev->dev;
|
||||
smcgp->smc = smc;
|
||||
|
||||
smcgp->first_index = macsmc_gpio_find_first_gpio_index(smcgp);
|
||||
if (smcgp->first_index < 0)
|
||||
return smcgp->first_index;
|
||||
|
||||
ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (key > macsmc_gpio_key(MAX_GPIO - 1))
|
||||
return -ENODEV;
|
||||
|
||||
dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key);
|
||||
|
||||
smcgp->gc.label = "macsmc-pmu-gpio";
|
||||
smcgp->gc.owner = THIS_MODULE;
|
||||
smcgp->gc.get = macsmc_gpio_get;
|
||||
smcgp->gc.set_rv = macsmc_gpio_set;
|
||||
smcgp->gc.get_direction = macsmc_gpio_get_direction;
|
||||
smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask;
|
||||
smcgp->gc.can_sleep = true;
|
||||
smcgp->gc.ngpio = MAX_GPIO;
|
||||
smcgp->gc.base = -1;
|
||||
smcgp->gc.parent = &pdev->dev;
|
||||
|
||||
return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp);
|
||||
}
|
||||
|
||||
static const struct of_device_id macsmc_gpio_of_table[] = {
|
||||
{ .compatible = "apple,smc-gpio", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table);
|
||||
|
||||
static struct platform_driver macsmc_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "macsmc-gpio",
|
||||
.of_match_table = macsmc_gpio_of_table,
|
||||
},
|
||||
.probe = macsmc_gpio_probe,
|
||||
};
|
||||
module_platform_driver(macsmc_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_DESCRIPTION("Apple SMC GPIO driver");
|
||||
@@ -285,6 +285,24 @@ config MFD_CS42L43_SDW
|
||||
Select this to support the Cirrus Logic CS42L43 PC CODEC with
|
||||
headphone and class D speaker drivers over SoundWire.
|
||||
|
||||
config MFD_MACSMC
|
||||
tristate "Apple Silicon System Management Controller (SMC)"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on APPLE_RTKIT
|
||||
select MFD_CORE
|
||||
help
|
||||
The System Management Controller (SMC) on Apple Silicon machines is a
|
||||
piece of hardware that exposes various functionalities such as
|
||||
temperature sensors, voltage/power meters, shutdown/reboot handling,
|
||||
GPIOs and more.
|
||||
|
||||
Communication happens via a shared mailbox using the RTKit protocol
|
||||
which is also used for other co-processors. The SMC protocol then
|
||||
allows reading and writing many different keys which implement the
|
||||
various features. The MFD core device handles this protocol and
|
||||
exposes it to the sub-devices.
|
||||
|
||||
config MFD_MADERA
|
||||
tristate "Cirrus Logic Madera codecs"
|
||||
select MFD_CORE
|
||||
|
||||
@@ -21,6 +21,7 @@ obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o
|
||||
obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o
|
||||
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
|
||||
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o
|
||||
obj-$(CONFIG_MFD_MACSMC) += macsmc.o
|
||||
|
||||
obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o
|
||||
obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o
|
||||
|
||||
498
drivers/mfd/macsmc.c
Normal file
498
drivers/mfd/macsmc.c
Normal file
@@ -0,0 +1,498 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* Apple SMC (System Management Controller) MFD driver
|
||||
*
|
||||
* Copyright The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/macsmc.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/soc/apple/rtkit.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#define SMC_ENDPOINT 0x20
|
||||
|
||||
/* We don't actually know the true size here but this seem reasonable */
|
||||
#define SMC_SHMEM_SIZE 0x1000
|
||||
#define SMC_MAX_SIZE 255
|
||||
|
||||
#define SMC_MSG_READ_KEY 0x10
|
||||
#define SMC_MSG_WRITE_KEY 0x11
|
||||
#define SMC_MSG_GET_KEY_BY_INDEX 0x12
|
||||
#define SMC_MSG_GET_KEY_INFO 0x13
|
||||
#define SMC_MSG_INITIALIZE 0x17
|
||||
#define SMC_MSG_NOTIFICATION 0x18
|
||||
#define SMC_MSG_RW_KEY 0x20
|
||||
|
||||
#define SMC_DATA GENMASK_ULL(63, 32)
|
||||
#define SMC_WSIZE GENMASK_ULL(31, 24)
|
||||
#define SMC_SIZE GENMASK_ULL(23, 16)
|
||||
#define SMC_ID GENMASK_ULL(15, 12)
|
||||
#define SMC_MSG GENMASK_ULL(7, 0)
|
||||
#define SMC_RESULT SMC_MSG
|
||||
|
||||
#define SMC_TIMEOUT_MS 500
|
||||
|
||||
static const struct mfd_cell apple_smc_devs[] = {
|
||||
MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
|
||||
MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),
|
||||
};
|
||||
|
||||
static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,
|
||||
u64 size, u64 wsize, u32 *ret_data)
|
||||
{
|
||||
u8 result;
|
||||
int ret;
|
||||
u64 msg;
|
||||
|
||||
lockdep_assert_held(&smc->mutex);
|
||||
|
||||
if (smc->boot_stage != APPLE_SMC_INITIALIZED)
|
||||
return -EIO;
|
||||
if (smc->atomic_mode)
|
||||
return -EIO;
|
||||
|
||||
reinit_completion(&smc->cmd_done);
|
||||
|
||||
smc->msg_id = (smc->msg_id + 1) & 0xf;
|
||||
msg = (FIELD_PREP(SMC_MSG, cmd) |
|
||||
FIELD_PREP(SMC_SIZE, size) |
|
||||
FIELD_PREP(SMC_WSIZE, wsize) |
|
||||
FIELD_PREP(SMC_ID, smc->msg_id) |
|
||||
FIELD_PREP(SMC_DATA, arg));
|
||||
|
||||
ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, false);
|
||||
if (ret) {
|
||||
dev_err(smc->dev, "Failed to send command\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wait_for_completion_timeout(&smc->cmd_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) <= 0) {
|
||||
dev_err(smc->dev, "Command timed out (%llx)", msg);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) {
|
||||
dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n",
|
||||
smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
result = FIELD_GET(SMC_RESULT, smc->cmd_ret);
|
||||
if (result)
|
||||
return -EIO;
|
||||
|
||||
if (ret_data)
|
||||
*ret_data = FIELD_GET(SMC_DATA, smc->cmd_ret);
|
||||
|
||||
return FIELD_GET(SMC_SIZE, smc->cmd_ret);
|
||||
}
|
||||
|
||||
static int apple_smc_cmd(struct apple_smc *smc, u64 cmd, u64 arg,
|
||||
u64 size, u64 wsize, u32 *ret_data)
|
||||
{
|
||||
guard(mutex)(&smc->mutex);
|
||||
|
||||
return apple_smc_cmd_locked(smc, cmd, arg, size, wsize, ret_data);
|
||||
}
|
||||
|
||||
static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
|
||||
const void *wbuf, size_t wsize,
|
||||
void *rbuf, size_t rsize)
|
||||
{
|
||||
u64 smc_size, smc_wsize;
|
||||
u32 rdata;
|
||||
int ret;
|
||||
u64 cmd;
|
||||
|
||||
lockdep_assert_held(&smc->mutex);
|
||||
|
||||
if (rsize > SMC_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
if (wsize > SMC_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (rsize && wsize) {
|
||||
cmd = SMC_MSG_RW_KEY;
|
||||
memcpy_toio(smc->shmem.iomem, wbuf, wsize);
|
||||
smc_size = rsize;
|
||||
smc_wsize = wsize;
|
||||
} else if (wsize && !rsize) {
|
||||
cmd = SMC_MSG_WRITE_KEY;
|
||||
memcpy_toio(smc->shmem.iomem, wbuf, wsize);
|
||||
/*
|
||||
* Setting size to the length we want to write and wsize to 0
|
||||
* looks silly but that's how the SMC protocol works ¯\_(ツ)_/¯
|
||||
*/
|
||||
smc_size = wsize;
|
||||
smc_wsize = 0;
|
||||
} else if (!wsize && rsize) {
|
||||
cmd = SMC_MSG_READ_KEY;
|
||||
smc_size = rsize;
|
||||
smc_wsize = 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = apple_smc_cmd_locked(smc, cmd, key, smc_size, smc_wsize, &rdata);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (rsize) {
|
||||
/*
|
||||
* Small data <= 4 bytes is returned as part of the reply
|
||||
* message which is sent over the mailbox FIFO. Everything
|
||||
* bigger has to be copied from SRAM which is mapped as
|
||||
* Device memory.
|
||||
*/
|
||||
if (rsize <= 4)
|
||||
memcpy(rbuf, &rdata, rsize);
|
||||
else
|
||||
memcpy_fromio(rbuf, smc->shmem.iomem, rsize);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
|
||||
{
|
||||
guard(mutex)(&smc->mutex);
|
||||
|
||||
return apple_smc_rw_locked(smc, key, NULL, 0, buf, size);
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_read);
|
||||
|
||||
int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size)
|
||||
{
|
||||
guard(mutex)(&smc->mutex);
|
||||
|
||||
return apple_smc_rw_locked(smc, key, buf, size, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_write);
|
||||
|
||||
int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize,
|
||||
void *rbuf, size_t rsize)
|
||||
{
|
||||
guard(mutex)(&smc->mutex);
|
||||
|
||||
return apple_smc_rw_locked(smc, key, wbuf, wsize, rbuf, rsize);
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_rw);
|
||||
|
||||
int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key);
|
||||
|
||||
*key = swab32(*key);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_get_key_by_index);
|
||||
|
||||
int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info)
|
||||
{
|
||||
u8 key_info[6];
|
||||
int ret;
|
||||
|
||||
ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL);
|
||||
if (ret >= 0 && info) {
|
||||
memcpy_fromio(key_info, smc->shmem.iomem, sizeof(key_info));
|
||||
info->size = key_info[0];
|
||||
info->type_code = get_unaligned_be32(&key_info[1]);
|
||||
info->flags = key_info[5];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_get_key_info);
|
||||
|
||||
int apple_smc_enter_atomic(struct apple_smc *smc)
|
||||
{
|
||||
guard(mutex)(&smc->mutex);
|
||||
|
||||
/*
|
||||
* Disable notifications since this is called before shutdown and no
|
||||
* notification handler will be able to handle the notification
|
||||
* using atomic operations only. Also ignore any failure here
|
||||
* because we're about to shut down or reboot anyway.
|
||||
* We can't use apple_smc_write_flag here since that would try to lock
|
||||
* smc->mutex again.
|
||||
*/
|
||||
const u8 flag = 0;
|
||||
|
||||
apple_smc_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0);
|
||||
|
||||
smc->atomic_mode = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_enter_atomic);
|
||||
|
||||
int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size)
|
||||
{
|
||||
guard(spinlock_irqsave)(&smc->lock);
|
||||
u8 result;
|
||||
int ret;
|
||||
u64 msg;
|
||||
|
||||
if (size > SMC_MAX_SIZE || size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (smc->boot_stage != APPLE_SMC_INITIALIZED)
|
||||
return -EIO;
|
||||
if (!smc->atomic_mode)
|
||||
return -EIO;
|
||||
|
||||
memcpy_toio(smc->shmem.iomem, buf, size);
|
||||
smc->msg_id = (smc->msg_id + 1) & 0xf;
|
||||
msg = (FIELD_PREP(SMC_MSG, SMC_MSG_WRITE_KEY) |
|
||||
FIELD_PREP(SMC_SIZE, size) |
|
||||
FIELD_PREP(SMC_ID, smc->msg_id) |
|
||||
FIELD_PREP(SMC_DATA, key));
|
||||
smc->atomic_pending = true;
|
||||
|
||||
ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, true);
|
||||
if (ret < 0) {
|
||||
dev_err(smc->dev, "Failed to send command (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (smc->atomic_pending) {
|
||||
ret = apple_rtkit_poll(smc->rtk);
|
||||
if (ret < 0) {
|
||||
dev_err(smc->dev, "RTKit poll failed (%llx)", msg);
|
||||
return ret;
|
||||
}
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) {
|
||||
dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n",
|
||||
smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
result = FIELD_GET(SMC_RESULT, smc->cmd_ret);
|
||||
if (result)
|
||||
return -EIO;
|
||||
|
||||
return FIELD_GET(SMC_SIZE, smc->cmd_ret);
|
||||
}
|
||||
EXPORT_SYMBOL(apple_smc_write_atomic);
|
||||
|
||||
static void apple_smc_rtkit_crashed(void *cookie, const void *bfr, size_t bfr_len)
|
||||
{
|
||||
struct apple_smc *smc = cookie;
|
||||
|
||||
smc->boot_stage = APPLE_SMC_ERROR_CRASHED;
|
||||
dev_err(smc->dev, "SMC crashed! Your system will reboot in a few seconds...\n");
|
||||
}
|
||||
|
||||
static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr)
|
||||
{
|
||||
struct apple_smc *smc = cookie;
|
||||
size_t bfr_end;
|
||||
|
||||
if (!bfr->iova) {
|
||||
dev_err(smc->dev, "RTKit wants a RAM buffer\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (check_add_overflow(bfr->iova, bfr->size - 1, &bfr_end))
|
||||
return -EFAULT;
|
||||
|
||||
if (bfr->iova < smc->sram->start || bfr->iova > smc->sram->end ||
|
||||
bfr_end > smc->sram->end) {
|
||||
dev_err(smc->dev, "RTKit buffer request outside SRAM region: [0x%llx, 0x%llx]\n",
|
||||
(unsigned long long)bfr->iova,
|
||||
(unsigned long long)bfr_end);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
bfr->iomem = smc->sram_base + (bfr->iova - smc->sram->start);
|
||||
bfr->is_mapped = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message)
|
||||
{
|
||||
struct apple_smc *smc = cookie;
|
||||
|
||||
if (endpoint != SMC_ENDPOINT) {
|
||||
dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (smc->boot_stage == APPLE_SMC_BOOTING) {
|
||||
int ret;
|
||||
|
||||
smc->shmem.iova = message;
|
||||
smc->shmem.size = SMC_SHMEM_SIZE;
|
||||
ret = apple_smc_rtkit_shmem_setup(smc, &smc->shmem);
|
||||
if (ret < 0) {
|
||||
smc->boot_stage = APPLE_SMC_ERROR_NO_SHMEM;
|
||||
dev_err(smc->dev, "Failed to initialize shared memory (%d)\n", ret);
|
||||
} else {
|
||||
smc->boot_stage = APPLE_SMC_INITIALIZED;
|
||||
}
|
||||
complete(&smc->init_done);
|
||||
} else if (FIELD_GET(SMC_MSG, message) == SMC_MSG_NOTIFICATION) {
|
||||
/* Handle these in the RTKit worker thread */
|
||||
return false;
|
||||
} else {
|
||||
smc->cmd_ret = message;
|
||||
if (smc->atomic_pending)
|
||||
smc->atomic_pending = false;
|
||||
else
|
||||
complete(&smc->cmd_done);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message)
|
||||
{
|
||||
struct apple_smc *smc = cookie;
|
||||
|
||||
if (endpoint != SMC_ENDPOINT) {
|
||||
dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FIELD_GET(SMC_MSG, message) != SMC_MSG_NOTIFICATION) {
|
||||
dev_warn(smc->dev, "Received unknown message from worker: 0x%llx\n", message);
|
||||
return;
|
||||
}
|
||||
|
||||
blocking_notifier_call_chain(&smc->event_handlers, FIELD_GET(SMC_DATA, message), NULL);
|
||||
}
|
||||
|
||||
static const struct apple_rtkit_ops apple_smc_rtkit_ops = {
|
||||
.crashed = apple_smc_rtkit_crashed,
|
||||
.recv_message = apple_smc_rtkit_recv,
|
||||
.recv_message_early = apple_smc_rtkit_recv_early,
|
||||
.shmem_setup = apple_smc_rtkit_shmem_setup,
|
||||
};
|
||||
|
||||
static void apple_smc_rtkit_shutdown(void *data)
|
||||
{
|
||||
struct apple_smc *smc = data;
|
||||
|
||||
/* Shut down SMC firmware, if it's not completely wedged */
|
||||
if (apple_rtkit_is_running(smc->rtk))
|
||||
apple_rtkit_quiesce(smc->rtk);
|
||||
}
|
||||
|
||||
static void apple_smc_disable_notifications(void *data)
|
||||
{
|
||||
struct apple_smc *smc = data;
|
||||
|
||||
apple_smc_write_flag(smc, SMC_KEY(NTAP), false);
|
||||
}
|
||||
|
||||
static int apple_smc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct apple_smc *smc;
|
||||
u32 count;
|
||||
int ret;
|
||||
|
||||
smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL);
|
||||
if (!smc)
|
||||
return -ENOMEM;
|
||||
|
||||
smc->dev = &pdev->dev;
|
||||
smc->sram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &smc->sram);
|
||||
if (IS_ERR(smc->sram_base))
|
||||
return dev_err_probe(dev, PTR_ERR(smc->sram_base), "Failed to map SRAM region");
|
||||
|
||||
smc->rtk = devm_apple_rtkit_init(dev, smc, NULL, 0, &apple_smc_rtkit_ops);
|
||||
if (IS_ERR(smc->rtk))
|
||||
return dev_err_probe(dev, PTR_ERR(smc->rtk), "Failed to initialize RTKit");
|
||||
|
||||
smc->boot_stage = APPLE_SMC_BOOTING;
|
||||
ret = apple_rtkit_wake(smc->rtk);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to wake up SMC");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, apple_smc_rtkit_shutdown, smc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register rtkit shutdown action");
|
||||
|
||||
ret = apple_rtkit_start_ep(smc->rtk, SMC_ENDPOINT);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to start SMC endpoint");
|
||||
|
||||
init_completion(&smc->init_done);
|
||||
init_completion(&smc->cmd_done);
|
||||
|
||||
ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT,
|
||||
FIELD_PREP(SMC_MSG, SMC_MSG_INITIALIZE), NULL, false);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to send init message");
|
||||
|
||||
if (wait_for_completion_timeout(&smc->init_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) == 0) {
|
||||
dev_err(dev, "Timed out initializing SMC");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (smc->boot_stage != APPLE_SMC_INITIALIZED) {
|
||||
dev_err(dev, "SMC failed to boot successfully, boot stage=%d\n", smc->boot_stage);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, smc);
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers);
|
||||
|
||||
ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count);
|
||||
if (ret)
|
||||
return dev_err_probe(smc->dev, ret, "Failed to get key count");
|
||||
smc->key_count = be32_to_cpu(count);
|
||||
|
||||
/* Enable notifications */
|
||||
apple_smc_write_flag(smc, SMC_KEY(NTAP), true);
|
||||
ret = devm_add_action_or_reset(dev, apple_smc_disable_notifications, smc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register notification disable action");
|
||||
|
||||
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");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id apple_smc_of_match[] = {
|
||||
{ .compatible = "apple,smc" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, apple_smc_of_match);
|
||||
|
||||
static struct platform_driver apple_smc_driver = {
|
||||
.driver = {
|
||||
.name = "macsmc",
|
||||
.of_match_table = apple_smc_of_match,
|
||||
},
|
||||
.probe = apple_smc_probe,
|
||||
};
|
||||
module_platform_driver(apple_smc_driver);
|
||||
|
||||
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
|
||||
MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_DESCRIPTION("Apple SMC driver");
|
||||
@@ -1,6 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Core functions for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs
|
||||
* Core functions for following TI PMICs:
|
||||
* - LP8764
|
||||
* - TPS65224
|
||||
* - TPS652G1
|
||||
* - TPS6593
|
||||
* - TPS6594
|
||||
*
|
||||
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
|
||||
*/
|
||||
@@ -414,6 +419,61 @@ static const unsigned int tps65224_irq_reg[] = {
|
||||
TPS6594_REG_INT_FSM_ERR,
|
||||
};
|
||||
|
||||
/* TPS652G1 Resources */
|
||||
|
||||
static const struct mfd_cell tps652g1_common_cells[] = {
|
||||
MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources),
|
||||
MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources),
|
||||
MFD_CELL_NAME("tps6594-regulator"),
|
||||
};
|
||||
|
||||
static const struct regmap_irq tps652g1_irqs[] = {
|
||||
/* INT_GPIO register */
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_GPIO1, 2, TPS65224_BIT_GPIO1_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_GPIO2, 2, TPS65224_BIT_GPIO2_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_GPIO3, 2, TPS65224_BIT_GPIO3_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_GPIO4, 2, TPS65224_BIT_GPIO4_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_GPIO5, 2, TPS65224_BIT_GPIO5_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_GPIO6, 2, TPS65224_BIT_GPIO6_INT),
|
||||
|
||||
/* INT_STARTUP register */
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_VSENSE, 3, TPS65224_BIT_VSENSE_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_ENABLE, 3, TPS6594_BIT_ENABLE_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_PB_SHORT, 3, TPS65224_BIT_PB_SHORT_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_FSD, 3, TPS6594_BIT_FSD_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_SOFT_REBOOT, 3, TPS6594_BIT_SOFT_REBOOT_INT),
|
||||
|
||||
/* INT_MISC register */
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_BIST_PASS, 4, TPS6594_BIT_BIST_PASS_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_EXT_CLK, 4, TPS6594_BIT_EXT_CLK_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_REG_UNLOCK, 4, TPS65224_BIT_REG_UNLOCK_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_TWARN, 4, TPS6594_BIT_TWARN_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_PB_LONG, 4, TPS65224_BIT_PB_LONG_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_PB_FALL, 4, TPS65224_BIT_PB_FALL_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_PB_RISE, 4, TPS65224_BIT_PB_RISE_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_ADC_CONV_READY, 4, TPS65224_BIT_ADC_CONV_READY_INT),
|
||||
|
||||
/* INT_MODERATE_ERR register */
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_TSD_ORD, 5, TPS6594_BIT_TSD_ORD_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_BIST_FAIL, 5, TPS6594_BIT_BIST_FAIL_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_REG_CRC_ERR, 5, TPS6594_BIT_REG_CRC_ERR_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_RECOV_CNT, 5, TPS6594_BIT_RECOV_CNT_INT),
|
||||
|
||||
/* INT_SEVERE_ERR register */
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_TSD_IMM, 6, TPS6594_BIT_TSD_IMM_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_OVP, 6, TPS6594_BIT_VCCA_OVP_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_PFSM_ERR, 6, TPS6594_BIT_PFSM_ERR_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_BG_XMON, 6, TPS65224_BIT_BG_XMON_INT),
|
||||
|
||||
/* INT_FSM_ERR register */
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_IMM_SHUTDOWN, 7, TPS6594_BIT_IMM_SHUTDOWN_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_ORD_SHUTDOWN, 7, TPS6594_BIT_ORD_SHUTDOWN_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_MCU_PWR_ERR, 7, TPS6594_BIT_MCU_PWR_ERR_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_SOC_PWR_ERR, 7, TPS6594_BIT_SOC_PWR_ERR_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_COMM_ERR, 7, TPS6594_BIT_COMM_ERR_INT),
|
||||
REGMAP_IRQ_REG(TPS65224_IRQ_I2C2_ERR, 7, TPS65224_BIT_I2C2_ERR_INT),
|
||||
};
|
||||
|
||||
static inline unsigned int tps6594_get_irq_reg(struct regmap_irq_chip_data *data,
|
||||
unsigned int base, int index)
|
||||
{
|
||||
@@ -443,7 +503,7 @@ static int tps6594_handle_post_irq(void *irq_drv_data)
|
||||
* a new interrupt.
|
||||
*/
|
||||
if (tps->use_crc) {
|
||||
if (tps->chip_id == TPS65224) {
|
||||
if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
|
||||
regmap_reg = TPS6594_REG_INT_FSM_ERR;
|
||||
mask_val = TPS6594_BIT_COMM_ERR_INT;
|
||||
} else {
|
||||
@@ -481,6 +541,18 @@ static struct regmap_irq_chip tps65224_irq_chip = {
|
||||
.handle_post_irq = tps6594_handle_post_irq,
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip tps652g1_irq_chip = {
|
||||
.ack_base = TPS6594_REG_INT_BUCK,
|
||||
.ack_invert = 1,
|
||||
.clear_ack = 1,
|
||||
.init_ack_masked = 1,
|
||||
.num_regs = ARRAY_SIZE(tps65224_irq_reg),
|
||||
.irqs = tps652g1_irqs,
|
||||
.num_irqs = ARRAY_SIZE(tps652g1_irqs),
|
||||
.get_irq_reg = tps65224_get_irq_reg,
|
||||
.handle_post_irq = tps6594_handle_post_irq,
|
||||
};
|
||||
|
||||
static const struct regmap_range tps6594_volatile_ranges[] = {
|
||||
regmap_reg_range(TPS6594_REG_INT_TOP, TPS6594_REG_STAT_READBACK_ERR),
|
||||
regmap_reg_range(TPS6594_REG_RTC_STATUS, TPS6594_REG_RTC_STATUS),
|
||||
@@ -507,7 +579,7 @@ static int tps6594_check_crc_mode(struct tps6594 *tps, bool primary_pmic)
|
||||
int ret;
|
||||
unsigned int regmap_reg, mask_val;
|
||||
|
||||
if (tps->chip_id == TPS65224) {
|
||||
if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
|
||||
regmap_reg = TPS6594_REG_CONFIG_2;
|
||||
mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN;
|
||||
} else {
|
||||
@@ -537,7 +609,7 @@ static int tps6594_set_crc_feature(struct tps6594 *tps)
|
||||
int ret;
|
||||
unsigned int regmap_reg, mask_val;
|
||||
|
||||
if (tps->chip_id == TPS65224) {
|
||||
if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
|
||||
regmap_reg = TPS6594_REG_CONFIG_2;
|
||||
mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN;
|
||||
} else {
|
||||
@@ -628,6 +700,10 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc)
|
||||
irq_chip = &tps65224_irq_chip;
|
||||
n_cells = ARRAY_SIZE(tps65224_common_cells);
|
||||
cells = tps65224_common_cells;
|
||||
} else if (tps->chip_id == TPS652G1) {
|
||||
irq_chip = &tps652g1_irq_chip;
|
||||
n_cells = ARRAY_SIZE(tps652g1_common_cells);
|
||||
cells = tps652g1_common_cells;
|
||||
} else {
|
||||
irq_chip = &tps6594_irq_chip;
|
||||
n_cells = ARRAY_SIZE(tps6594_common_cells);
|
||||
@@ -651,8 +727,8 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc)
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to add common child devices\n");
|
||||
|
||||
/* No RTC for LP8764 and TPS65224 */
|
||||
if (tps->chip_id != LP8764 && tps->chip_id != TPS65224) {
|
||||
/* No RTC for LP8764, TPS65224 and TPS652G1 */
|
||||
if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) {
|
||||
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells,
|
||||
ARRAY_SIZE(tps6594_rtc_cells), NULL, 0,
|
||||
regmap_irq_get_domain(tps->irq_data));
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* I2C access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs
|
||||
* I2C access driver for the following TI PMICs:
|
||||
* - LP8764
|
||||
* - TPS65224
|
||||
* - TPS652G1
|
||||
* - TPS6593
|
||||
* - TPS6594
|
||||
*
|
||||
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
|
||||
*/
|
||||
@@ -197,6 +202,7 @@ static const struct of_device_id tps6594_i2c_of_match_table[] = {
|
||||
{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
|
||||
{ .compatible = "ti,lp8764-q1", .data = (void *)LP8764, },
|
||||
{ .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, },
|
||||
{ .compatible = "ti,tps652g1", .data = (void *)TPS652G1, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table);
|
||||
@@ -222,7 +228,7 @@ static int tps6594_i2c_probe(struct i2c_client *client)
|
||||
return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
|
||||
tps->chip_id = (unsigned long)match->data;
|
||||
|
||||
if (tps->chip_id == TPS65224)
|
||||
if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1)
|
||||
tps6594_i2c_regmap_config.volatile_table = &tps65224_volatile_table;
|
||||
|
||||
tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config);
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SPI access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs
|
||||
* SPI access driver for the following TI PMICs:
|
||||
* - LP8764
|
||||
* - TPS65224
|
||||
* - TPS652G1
|
||||
* - TPS6593
|
||||
* - TPS6594
|
||||
*
|
||||
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
|
||||
*/
|
||||
@@ -82,6 +87,7 @@ static const struct of_device_id tps6594_spi_of_match_table[] = {
|
||||
{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
|
||||
{ .compatible = "ti,lp8764-q1", .data = (void *)LP8764, },
|
||||
{ .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, },
|
||||
{ .compatible = "ti,tps652g1", .data = (void *)TPS652G1, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table);
|
||||
@@ -107,7 +113,7 @@ static int tps6594_spi_probe(struct spi_device *spi)
|
||||
return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
|
||||
tps->chip_id = (unsigned long)match->data;
|
||||
|
||||
if (tps->chip_id == TPS65224)
|
||||
if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1)
|
||||
tps6594_spi_regmap_config.volatile_table = &tps65224_volatile_table;
|
||||
|
||||
tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config);
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PFSM (Pre-configurable Finite State Machine) driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs
|
||||
* PFSM (Pre-configurable Finite State Machine) driver for the following
|
||||
* PMICs:
|
||||
* - LP8764
|
||||
* - TPS65224
|
||||
* - TPS652G1
|
||||
* - TPS6594
|
||||
* - TPS6593
|
||||
*
|
||||
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
|
||||
*/
|
||||
@@ -141,7 +147,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a
|
||||
switch (cmd) {
|
||||
case PMIC_GOTO_STANDBY:
|
||||
/* Disable LP mode on TPS6594 Family PMIC */
|
||||
if (pfsm->chip_id != TPS65224) {
|
||||
if (pfsm->chip_id != TPS65224 && pfsm->chip_id != TPS652G1) {
|
||||
ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2,
|
||||
TPS6594_BIT_LP_STANDBY_SEL);
|
||||
|
||||
@@ -154,8 +160,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a
|
||||
TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0));
|
||||
break;
|
||||
case PMIC_GOTO_LP_STANDBY:
|
||||
/* TPS65224 does not support LP STANDBY */
|
||||
if (pfsm->chip_id == TPS65224)
|
||||
/* TPS65224/TPS652G1 does not support LP STANDBY */
|
||||
if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1)
|
||||
return ret;
|
||||
|
||||
/* Enable LP mode */
|
||||
@@ -179,8 +185,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a
|
||||
TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B);
|
||||
break;
|
||||
case PMIC_SET_MCU_ONLY_STATE:
|
||||
/* TPS65224 does not support MCU_ONLY_STATE */
|
||||
if (pfsm->chip_id == TPS65224)
|
||||
/* TPS65224/TPS652G1 does not support MCU_ONLY_STATE */
|
||||
if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1)
|
||||
return ret;
|
||||
|
||||
if (copy_from_user(&state_opt, argp, sizeof(state_opt)))
|
||||
@@ -206,7 +212,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a
|
||||
return -EFAULT;
|
||||
|
||||
/* Configure wake-up destination */
|
||||
if (pfsm->chip_id == TPS65224) {
|
||||
if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) {
|
||||
regmap_reg = TPS65224_REG_STARTUP_CTRL;
|
||||
mask = TPS65224_MASK_STARTUP_DEST;
|
||||
} else {
|
||||
@@ -230,9 +236,14 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a
|
||||
return ret;
|
||||
|
||||
/* Modify NSLEEP1-2 bits */
|
||||
ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
|
||||
pfsm->chip_id == TPS65224 ?
|
||||
TPS6594_BIT_NSLEEP1B : TPS6594_BIT_NSLEEP2B);
|
||||
if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1)
|
||||
ret = regmap_clear_bits(pfsm->regmap,
|
||||
TPS6594_REG_FSM_NSLEEP_TRIGGERS,
|
||||
TPS6594_BIT_NSLEEP1B);
|
||||
else
|
||||
ret = regmap_clear_bits(pfsm->regmap,
|
||||
TPS6594_REG_FSM_NSLEEP_TRIGGERS,
|
||||
TPS6594_BIT_NSLEEP2B);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,10 @@ static const char *const tps65224_nerr_mcu_func_group_names[] = {
|
||||
"GPIO5",
|
||||
};
|
||||
|
||||
static const char *const tps652g1_cs_spi_func_group_names[] = {
|
||||
"GPIO1",
|
||||
};
|
||||
|
||||
struct tps6594_pinctrl_function {
|
||||
struct pinfunction pinfunction;
|
||||
u8 muxval;
|
||||
@@ -287,6 +291,18 @@ static const struct tps6594_pinctrl_function tps65224_pinctrl_functions[] = {
|
||||
FUNCTION(tps65224, nerr_mcu, TPS65224_PINCTRL_NERR_MCU_FUNCTION),
|
||||
};
|
||||
|
||||
static const struct tps6594_pinctrl_function tps652g1_pinctrl_functions[] = {
|
||||
FUNCTION(tps65224, gpio, TPS6594_PINCTRL_GPIO_FUNCTION),
|
||||
FUNCTION(tps65224, sda_i2c2_sdo_spi, TPS65224_PINCTRL_SDA_I2C2_SDO_SPI_FUNCTION),
|
||||
FUNCTION(tps65224, nsleep2, TPS65224_PINCTRL_NSLEEP2_FUNCTION),
|
||||
FUNCTION(tps65224, nint, TPS65224_PINCTRL_NINT_FUNCTION),
|
||||
FUNCTION(tps652g1, cs_spi, TPS65224_PINCTRL_SCL_I2C2_CS_SPI_FUNCTION),
|
||||
FUNCTION(tps65224, nsleep1, TPS65224_PINCTRL_NSLEEP1_FUNCTION),
|
||||
FUNCTION(tps65224, pb, TPS65224_PINCTRL_PB_FUNCTION),
|
||||
FUNCTION(tps65224, wkup, TPS65224_PINCTRL_WKUP_FUNCTION),
|
||||
FUNCTION(tps65224, syncclkin, TPS65224_PINCTRL_SYNCCLKIN_FUNCTION),
|
||||
};
|
||||
|
||||
struct tps6594_pinctrl {
|
||||
struct tps6594 *tps;
|
||||
struct gpio_regmap *gpio_regmap;
|
||||
@@ -300,6 +316,16 @@ struct tps6594_pinctrl {
|
||||
struct muxval_remap *remap;
|
||||
};
|
||||
|
||||
static struct tps6594_pinctrl tps652g1_template_pinctrl = {
|
||||
.funcs = tps652g1_pinctrl_functions,
|
||||
.func_cnt = ARRAY_SIZE(tps652g1_pinctrl_functions),
|
||||
.pins = tps65224_pins,
|
||||
.num_pins = ARRAY_SIZE(tps65224_pins),
|
||||
.mux_sel_mask = TPS65224_MASK_GPIO_SEL,
|
||||
.remap = tps65224_muxval_remap,
|
||||
.remap_cnt = ARRAY_SIZE(tps65224_muxval_remap),
|
||||
};
|
||||
|
||||
static struct tps6594_pinctrl tps65224_template_pinctrl = {
|
||||
.funcs = tps65224_pinctrl_functions,
|
||||
.func_cnt = ARRAY_SIZE(tps65224_pinctrl_functions),
|
||||
@@ -475,6 +501,15 @@ static int tps6594_pinctrl_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (tps->chip_id) {
|
||||
case TPS652G1:
|
||||
pctrl_desc->pins = tps65224_pins;
|
||||
pctrl_desc->npins = ARRAY_SIZE(tps65224_pins);
|
||||
|
||||
*pinctrl = tps652g1_template_pinctrl;
|
||||
|
||||
config.ngpio = ARRAY_SIZE(tps65224_gpio_func_group_names);
|
||||
config.ngpio_per_reg = TPS65224_NGPIO_PER_REG;
|
||||
break;
|
||||
case TPS65224:
|
||||
pctrl_desc->pins = tps65224_pins;
|
||||
pctrl_desc->npins = ARRAY_SIZE(tps65224_pins);
|
||||
|
||||
@@ -128,6 +128,15 @@ config POWER_RESET_LINKSTATION
|
||||
|
||||
Say Y here if you have a Buffalo LinkStation LS421D/E.
|
||||
|
||||
config POWER_RESET_MACSMC
|
||||
tristate "Apple SMC reset/power-off driver"
|
||||
depends on MFD_MACSMC
|
||||
help
|
||||
This driver supports reset and power-off on Apple Mac machines
|
||||
that implement this functionality via the SMC.
|
||||
|
||||
Say Y here if you have an Apple Silicon Mac.
|
||||
|
||||
config POWER_RESET_MSM
|
||||
bool "Qualcomm MSM power-off driver"
|
||||
depends on ARCH_QCOM
|
||||
|
||||
@@ -13,6 +13,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
|
||||
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_MACSMC) += macsmc-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o
|
||||
|
||||
290
drivers/power/reset/macsmc-reboot.c
Normal file
290
drivers/power/reset/macsmc-reboot.c
Normal file
@@ -0,0 +1,290 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* Apple SMC Reboot/Poweroff Handler
|
||||
* Copyright The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/macsmc.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct macsmc_reboot_nvmem {
|
||||
struct nvmem_cell *shutdown_flag;
|
||||
struct nvmem_cell *boot_stage;
|
||||
struct nvmem_cell *boot_error_count;
|
||||
struct nvmem_cell *panic_count;
|
||||
};
|
||||
|
||||
static const char * const nvmem_names[] = {
|
||||
"shutdown_flag",
|
||||
"boot_stage",
|
||||
"boot_error_count",
|
||||
"panic_count",
|
||||
};
|
||||
|
||||
enum boot_stage {
|
||||
BOOT_STAGE_SHUTDOWN = 0x00, /* Clean shutdown */
|
||||
BOOT_STAGE_IBOOT_DONE = 0x2f, /* Last stage of bootloader */
|
||||
BOOT_STAGE_KERNEL_STARTED = 0x30, /* Normal OS booting */
|
||||
};
|
||||
|
||||
struct macsmc_reboot {
|
||||
struct device *dev;
|
||||
struct apple_smc *smc;
|
||||
struct notifier_block reboot_notify;
|
||||
|
||||
union {
|
||||
struct macsmc_reboot_nvmem nvm;
|
||||
struct nvmem_cell *nvm_cells[ARRAY_SIZE(nvmem_names)];
|
||||
};
|
||||
};
|
||||
|
||||
/* Helpers to read/write a u8 given a struct nvmem_cell */
|
||||
static int nvmem_cell_get_u8(struct nvmem_cell *cell)
|
||||
{
|
||||
size_t len;
|
||||
void *bfr;
|
||||
u8 val;
|
||||
|
||||
bfr = nvmem_cell_read(cell, &len);
|
||||
if (IS_ERR(bfr))
|
||||
return PTR_ERR(bfr);
|
||||
|
||||
if (len < 1) {
|
||||
kfree(bfr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = *(u8 *)bfr;
|
||||
kfree(bfr);
|
||||
return val;
|
||||
}
|
||||
|
||||
static int nvmem_cell_set_u8(struct nvmem_cell *cell, u8 val)
|
||||
{
|
||||
return nvmem_cell_write(cell, &val, sizeof(val));
|
||||
}
|
||||
|
||||
/*
|
||||
* SMC 'MBSE' key actions:
|
||||
*
|
||||
* 'offw' - shutdown warning
|
||||
* 'slpw' - sleep warning
|
||||
* 'rest' - restart warning
|
||||
* 'off1' - shutdown (needs PMU bit set to stay on)
|
||||
* 'susp' - suspend
|
||||
* 'phra' - restart ("PE Halt Restart Action"?)
|
||||
* 'panb' - panic beginning
|
||||
* 'pane' - panic end
|
||||
*/
|
||||
|
||||
static int macsmc_prepare_atomic(struct sys_off_data *data)
|
||||
{
|
||||
struct macsmc_reboot *reboot = data->cb_data;
|
||||
|
||||
dev_info(reboot->dev, "Preparing SMC for atomic mode\n");
|
||||
|
||||
apple_smc_enter_atomic(reboot->smc);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int macsmc_power_off(struct sys_off_data *data)
|
||||
{
|
||||
struct macsmc_reboot *reboot = data->cb_data;
|
||||
|
||||
dev_info(reboot->dev, "Issuing power off (off1)\n");
|
||||
|
||||
if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(off1)) < 0) {
|
||||
dev_err(reboot->dev, "Failed to issue MBSE = off1 (power_off)\n");
|
||||
} else {
|
||||
mdelay(100);
|
||||
WARN_ONCE(1, "Unable to power off system\n");
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int macsmc_restart(struct sys_off_data *data)
|
||||
{
|
||||
struct macsmc_reboot *reboot = data->cb_data;
|
||||
|
||||
dev_info(reboot->dev, "Issuing restart (phra)\n");
|
||||
|
||||
if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(phra)) < 0) {
|
||||
dev_err(reboot->dev, "Failed to issue MBSE = phra (restart)\n");
|
||||
} else {
|
||||
mdelay(100);
|
||||
WARN_ONCE(1, "Unable to restart system\n");
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int macsmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data)
|
||||
{
|
||||
struct macsmc_reboot *reboot = container_of(this, struct macsmc_reboot, reboot_notify);
|
||||
u8 shutdown_flag;
|
||||
u32 val;
|
||||
|
||||
switch (action) {
|
||||
case SYS_RESTART:
|
||||
val = SMC_KEY(rest);
|
||||
shutdown_flag = 0;
|
||||
break;
|
||||
case SYS_POWER_OFF:
|
||||
val = SMC_KEY(offw);
|
||||
shutdown_flag = 1;
|
||||
break;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
dev_info(reboot->dev, "Preparing for reboot (%p4ch)\n", &val);
|
||||
|
||||
/* On the Mac Mini, this will turn off the LED for power off */
|
||||
if (apple_smc_write_u32(reboot->smc, SMC_KEY(MBSE), val) < 0)
|
||||
dev_err(reboot->dev, "Failed to issue MBSE = %p4ch (reboot_prepare)\n", &val);
|
||||
|
||||
/* Set the boot_stage to 0, which means we're doing a clean shutdown/reboot. */
|
||||
if (reboot->nvm.boot_stage &&
|
||||
nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_SHUTDOWN) < 0)
|
||||
dev_err(reboot->dev, "Failed to write boot_stage\n");
|
||||
|
||||
/*
|
||||
* Set the PMU flag to actually reboot into the off state.
|
||||
* Without this, the device will just reboot. We make it optional in case it is no longer
|
||||
* necessary on newer hardware.
|
||||
*/
|
||||
if (reboot->nvm.shutdown_flag &&
|
||||
nvmem_cell_set_u8(reboot->nvm.shutdown_flag, shutdown_flag) < 0)
|
||||
dev_err(reboot->dev, "Failed to write shutdown_flag\n");
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void macsmc_power_init_error_counts(struct macsmc_reboot *reboot)
|
||||
{
|
||||
int boot_error_count, panic_count;
|
||||
|
||||
if (!reboot->nvm.boot_error_count || !reboot->nvm.panic_count)
|
||||
return;
|
||||
|
||||
boot_error_count = nvmem_cell_get_u8(reboot->nvm.boot_error_count);
|
||||
if (boot_error_count < 0) {
|
||||
dev_err(reboot->dev, "Failed to read boot_error_count (%d)\n", boot_error_count);
|
||||
return;
|
||||
}
|
||||
|
||||
panic_count = nvmem_cell_get_u8(reboot->nvm.panic_count);
|
||||
if (panic_count < 0) {
|
||||
dev_err(reboot->dev, "Failed to read panic_count (%d)\n", panic_count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!boot_error_count && !panic_count)
|
||||
return;
|
||||
|
||||
dev_warn(reboot->dev, "PMU logged %d boot error(s) and %d panic(s)\n",
|
||||
boot_error_count, panic_count);
|
||||
|
||||
if (nvmem_cell_set_u8(reboot->nvm.panic_count, 0) < 0)
|
||||
dev_err(reboot->dev, "Failed to reset panic_count\n");
|
||||
if (nvmem_cell_set_u8(reboot->nvm.boot_error_count, 0) < 0)
|
||||
dev_err(reboot->dev, "Failed to reset boot_error_count\n");
|
||||
}
|
||||
|
||||
static int macsmc_reboot_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
|
||||
struct macsmc_reboot *reboot;
|
||||
int ret, i;
|
||||
|
||||
reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL);
|
||||
if (!reboot)
|
||||
return -ENOMEM;
|
||||
|
||||
reboot->dev = &pdev->dev;
|
||||
reboot->smc = smc;
|
||||
|
||||
platform_set_drvdata(pdev, reboot);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nvmem_names); i++) {
|
||||
struct nvmem_cell *cell;
|
||||
|
||||
cell = devm_nvmem_cell_get(&pdev->dev,
|
||||
nvmem_names[i]);
|
||||
if (IS_ERR(cell)) {
|
||||
if (PTR_ERR(cell) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n",
|
||||
nvmem_names[i], PTR_ERR(cell));
|
||||
/* Non fatal, we'll deal with it */
|
||||
cell = NULL;
|
||||
}
|
||||
reboot->nvm_cells[i] = cell;
|
||||
}
|
||||
|
||||
/* Set the boot_stage to indicate we're running the OS kernel */
|
||||
if (reboot->nvm.boot_stage &&
|
||||
nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_KERNEL_STARTED) < 0)
|
||||
dev_err(reboot->dev, "Failed to write boot_stage\n");
|
||||
|
||||
/* Display and clear the error counts */
|
||||
macsmc_power_init_error_counts(reboot);
|
||||
|
||||
reboot->reboot_notify.notifier_call = macsmc_reboot_notify;
|
||||
|
||||
ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF_PREPARE,
|
||||
SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to register power-off prepare handler\n");
|
||||
ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH,
|
||||
macsmc_power_off, reboot);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to register power-off handler\n");
|
||||
|
||||
ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART_PREPARE,
|
||||
SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to register restart prepare handler\n");
|
||||
ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH,
|
||||
macsmc_restart, reboot);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to register restart handler\n");
|
||||
|
||||
ret = devm_register_reboot_notifier(&pdev->dev, &reboot->reboot_notify);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to register reboot notifier\n");
|
||||
|
||||
dev_info(&pdev->dev, "Handling reboot and poweroff requests via SMC\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id macsmc_reboot_of_table[] = {
|
||||
{ .compatible = "apple,smc-reboot", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, macsmc_reboot_of_table);
|
||||
|
||||
static struct platform_driver macsmc_reboot_driver = {
|
||||
.driver = {
|
||||
.name = "macsmc-reboot",
|
||||
.of_match_table = macsmc_reboot_of_table,
|
||||
},
|
||||
.probe = macsmc_reboot_probe,
|
||||
};
|
||||
module_platform_driver(macsmc_reboot_driver);
|
||||
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_DESCRIPTION("Apple SMC reboot/poweroff driver");
|
||||
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
|
||||
@@ -279,8 +279,7 @@ static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk,
|
||||
dev_dbg(rtk->dev, "RTKit: buffer request for 0x%zx bytes at %pad\n",
|
||||
buffer->size, &buffer->iova);
|
||||
|
||||
if (buffer->iova &&
|
||||
(!rtk->ops->shmem_setup || !rtk->ops->shmem_destroy)) {
|
||||
if (buffer->iova && !rtk->ops->shmem_setup) {
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
279
include/linux/mfd/macsmc.h
Normal file
279
include/linux/mfd/macsmc.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/*
|
||||
* Apple SMC (System Management Controller) core definitions
|
||||
*
|
||||
* Copyright (C) The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_MFD_MACSMC_H
|
||||
#define _LINUX_MFD_MACSMC_H
|
||||
|
||||
#include <linux/soc/apple/rtkit.h>
|
||||
|
||||
/**
|
||||
* typedef smc_key - Alias for u32 to be used for SMC keys
|
||||
*
|
||||
* SMC keys are 32bit integers containing packed ASCII characters in natural
|
||||
* integer order, i.e. 0xAABBCCDD, which represent the FourCC ABCD.
|
||||
* The SMC driver is designed with this assumption and ensures the right
|
||||
* endianness is used when these are stored to memory and sent to or received
|
||||
* from the actual SMC firmware (which can be done in either shared memory or
|
||||
* as 64bit mailbox message on Apple Silicon).
|
||||
* Internally, SMC stores these keys in a table sorted lexicographically and
|
||||
* allows resolving an index into this table to the corresponding SMC key.
|
||||
* Thus, storing keys as u32 is very convenient as it allows to e.g. use
|
||||
* normal comparison operators which directly map to the natural order used
|
||||
* by SMC firmware.
|
||||
*
|
||||
* This simple type alias is introduced to allow easy recognition of SMC key
|
||||
* variables and arguments.
|
||||
*/
|
||||
typedef u32 smc_key;
|
||||
|
||||
/**
|
||||
* SMC_KEY - Convert FourCC SMC keys in source code to smc_key
|
||||
*
|
||||
* This macro can be used to easily define FourCC SMC keys in source code
|
||||
* and convert these to u32 / smc_key, e.g. SMC_KEY(NTAP) will expand to
|
||||
* 0x4e544150.
|
||||
*
|
||||
* @s: FourCC SMC key to be converted
|
||||
*/
|
||||
#define SMC_KEY(s) (smc_key)(_SMC_KEY(#s))
|
||||
#define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3])
|
||||
|
||||
#define APPLE_SMC_READABLE BIT(7)
|
||||
#define APPLE_SMC_WRITABLE BIT(6)
|
||||
#define APPLE_SMC_FUNCTION BIT(4)
|
||||
|
||||
/**
|
||||
* struct apple_smc_key_info - Information for a SMC key as returned by SMC
|
||||
* @type_code: FourCC code indicating the type for this key.
|
||||
* Known types:
|
||||
* ch8*: ASCII string
|
||||
* flag: Boolean, 1 or 0
|
||||
* flt: 32-bit single-precision IEEE 754 float
|
||||
* hex: Binary data
|
||||
* ioft: 64bit Unsigned fixed-point intger (48.16)
|
||||
* {si,ui}{8,16,32,64}: Signed/Unsigned 8-/16-/32-/64-bit integer
|
||||
* @size: Size of the buffer associated with this key
|
||||
* @flags: Bitfield encoding flags (APPLE_SMC_{READABLE,WRITABLE,FUNCTION})
|
||||
*/
|
||||
struct apple_smc_key_info {
|
||||
u32 type_code;
|
||||
u8 size;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum apple_smc_boot_stage - SMC boot stage
|
||||
* @APPLE_SMC_BOOTING: SMC is booting
|
||||
* @APPLE_SMC_INITIALIZED: SMC is initialized and ready to use
|
||||
* @APPLE_SMC_ERROR_NO_SHMEM: Shared memory could not be initialized during boot
|
||||
* @APPLE_SMC_ERROR_CRASHED: SMC has crashed
|
||||
*/
|
||||
enum apple_smc_boot_stage {
|
||||
APPLE_SMC_BOOTING,
|
||||
APPLE_SMC_INITIALIZED,
|
||||
APPLE_SMC_ERROR_NO_SHMEM,
|
||||
APPLE_SMC_ERROR_CRASHED
|
||||
};
|
||||
|
||||
/**
|
||||
* struct apple_smc
|
||||
* @dev: Underlying device struct for the physical backend device
|
||||
* @key_count: Number of available SMC keys
|
||||
* @first_key: First valid SMC key
|
||||
* @last_key: Last valid SMC key
|
||||
* @event_handlers: Notifier call chain for events received from SMC
|
||||
* @rtk: Pointer to Apple RTKit instance
|
||||
* @init_done: Completion for initialization
|
||||
* @boot_stage: Current boot stage of SMC
|
||||
* @sram: Pointer to SRAM resource
|
||||
* @sram_base: SRAM base address
|
||||
* @shmem: RTKit shared memory structure for SRAM
|
||||
* @msg_id: Current message id for commands, will be incremented for each command
|
||||
* @atomic_mode: Flag set when atomic mode is entered
|
||||
* @atomic_pending: Flag indicating pending atomic command
|
||||
* @cmd_done: Completion for command execution in non-atomic mode
|
||||
* @cmd_ret: Return value from SMC for last command
|
||||
* @mutex: Mutex for non-atomic mode
|
||||
* @lock: Spinlock for atomic mode
|
||||
*/
|
||||
struct apple_smc {
|
||||
struct device *dev;
|
||||
|
||||
u32 key_count;
|
||||
smc_key first_key;
|
||||
smc_key last_key;
|
||||
|
||||
struct blocking_notifier_head event_handlers;
|
||||
|
||||
struct apple_rtkit *rtk;
|
||||
|
||||
struct completion init_done;
|
||||
enum apple_smc_boot_stage boot_stage;
|
||||
|
||||
struct resource *sram;
|
||||
void __iomem *sram_base;
|
||||
struct apple_rtkit_shmem shmem;
|
||||
|
||||
unsigned int msg_id;
|
||||
|
||||
bool atomic_mode;
|
||||
bool atomic_pending;
|
||||
struct completion cmd_done;
|
||||
u64 cmd_ret;
|
||||
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* apple_smc_read - Read size bytes from given SMC key into buf
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @key: smc_key to be read
|
||||
* @buf: Buffer into which size bytes of data will be read from SMC
|
||||
* @size: Number of bytes to be read into buf
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size);
|
||||
|
||||
/**
|
||||
* apple_smc_write - Write size bytes into given SMC key from buf
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @key: smc_key data will be written to
|
||||
* @buf: Buffer from which size bytes of data will be written to SMC
|
||||
* @size: Number of bytes to be written
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size);
|
||||
|
||||
/**
|
||||
* apple_smc_enter_atomic - Enter atomic mode to be able to use apple_smc_write_atomic
|
||||
* @smc: Pointer to apple_smc struct
|
||||
*
|
||||
* This function switches the SMC backend to atomic mode which allows the
|
||||
* use of apple_smc_write_atomic while disabling *all* other functions.
|
||||
* This is only used for shutdown/reboot which requires writing to a SMC
|
||||
* key from atomic context.
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_enter_atomic(struct apple_smc *smc);
|
||||
|
||||
/**
|
||||
* apple_smc_write_atomic - Write size bytes into given SMC key from buf without sleeping
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @key: smc_key data will be written to
|
||||
* @buf: Buffer from which size bytes of data will be written to SMC
|
||||
* @size: Number of bytes to be written
|
||||
*
|
||||
* Note that this function will fail if apple_smc_enter_atomic hasn't been
|
||||
* called before.
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size);
|
||||
|
||||
/**
|
||||
* apple_smc_rw - Write and then read using the given SMC key
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @key: smc_key data will be written to
|
||||
* @wbuf: Buffer from which size bytes of data will be written to SMC
|
||||
* @wsize: Number of bytes to be written
|
||||
* @rbuf: Buffer to which size bytes of data will be read from SMC
|
||||
* @rsize: Number of bytes to be read
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize,
|
||||
void *rbuf, size_t rsize);
|
||||
|
||||
/**
|
||||
* apple_smc_get_key_by_index - Given an index return the corresponding SMC key
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @index: Index to be resolved
|
||||
* @key: Buffer for SMC key to be returned
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key);
|
||||
|
||||
/**
|
||||
* apple_smc_get_key_info - Get key information from SMC
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @key: Key to acquire information for
|
||||
* @info: Pointer to struct apple_smc_key_info which will be filled
|
||||
*
|
||||
* Return: Zero on success, negative errno on error
|
||||
*/
|
||||
int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info);
|
||||
|
||||
/**
|
||||
* apple_smc_key_exists - Check if the given SMC key exists
|
||||
* @smc: Pointer to apple_smc struct
|
||||
* @key: smc_key to be checked
|
||||
*
|
||||
* Return: True if the key exists, false otherwise
|
||||
*/
|
||||
static inline bool apple_smc_key_exists(struct apple_smc *smc, smc_key key)
|
||||
{
|
||||
return apple_smc_get_key_info(smc, key, NULL) >= 0;
|
||||
}
|
||||
|
||||
#define APPLE_SMC_TYPE_OPS(type) \
|
||||
static inline int apple_smc_read_##type(struct apple_smc *smc, smc_key key, type *p) \
|
||||
{ \
|
||||
int ret = apple_smc_read(smc, key, p, sizeof(*p)); \
|
||||
return (ret < 0) ? ret : ((ret != sizeof(*p)) ? -EINVAL : 0); \
|
||||
} \
|
||||
static inline int apple_smc_write_##type(struct apple_smc *smc, smc_key key, type p) \
|
||||
{ \
|
||||
return apple_smc_write(smc, key, &p, sizeof(p)); \
|
||||
} \
|
||||
static inline int apple_smc_write_##type##_atomic(struct apple_smc *smc, smc_key key, type p) \
|
||||
{ \
|
||||
return apple_smc_write_atomic(smc, key, &p, sizeof(p)); \
|
||||
} \
|
||||
static inline int apple_smc_rw_##type(struct apple_smc *smc, smc_key key, \
|
||||
type w, type *r) \
|
||||
{ \
|
||||
int ret = apple_smc_rw(smc, key, &w, sizeof(w), r, sizeof(*r)); \
|
||||
return (ret < 0) ? ret : ((ret != sizeof(*r)) ? -EINVAL : 0); \
|
||||
}
|
||||
|
||||
APPLE_SMC_TYPE_OPS(u64)
|
||||
APPLE_SMC_TYPE_OPS(u32)
|
||||
APPLE_SMC_TYPE_OPS(u16)
|
||||
APPLE_SMC_TYPE_OPS(u8)
|
||||
APPLE_SMC_TYPE_OPS(s64)
|
||||
APPLE_SMC_TYPE_OPS(s32)
|
||||
APPLE_SMC_TYPE_OPS(s16)
|
||||
APPLE_SMC_TYPE_OPS(s8)
|
||||
|
||||
static inline int apple_smc_read_flag(struct apple_smc *smc, smc_key key, bool *flag)
|
||||
{
|
||||
u8 val;
|
||||
int ret = apple_smc_read_u8(smc, key, &val);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*flag = val ? true : false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int apple_smc_write_flag(struct apple_smc *smc, smc_key key, bool state)
|
||||
{
|
||||
return apple_smc_write_u8(smc, key, state ? 1 : 0);
|
||||
}
|
||||
|
||||
static inline int apple_smc_write_flag_atomic(struct apple_smc *smc, smc_key key, bool state)
|
||||
{
|
||||
return apple_smc_write_u8_atomic(smc, key, state ? 1 : 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -19,6 +19,7 @@ enum pmic_id {
|
||||
TPS6593,
|
||||
LP8764,
|
||||
TPS65224,
|
||||
TPS652G1,
|
||||
};
|
||||
|
||||
/* Macro to get page index from register address */
|
||||
|
||||
Reference in New Issue
Block a user