mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-01 14:52:13 -05:00
Merge tag 'samsung-drivers-6.16' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into soc/drivers
Samsung SoC drivers for v6.16 Several improvements to Exynos ACPM (Alive Clock and Power Manager) driver: 1. Handle communication timeous better. 2. Avoid sleeping, so users (PMIC) can still transfer during system shutdown. 3. Fix reading longer messages from them firmware. 4. Deferred probe improvements. 5. Model the user of ACPM - PMIC - a as child device and export devm_acpm_get_by_node() for such use case. * tag 'samsung-drivers-6.16' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux: firmware: exynos-acpm: Correct kerneldoc and use typical np argument name firmware: exynos-acpm: introduce devm_acpm_get_by_node() firmware: exynos-acpm: populate devices from device tree data firmware: exynos-acpm: silence EPROBE_DEFER error on boot firmware: exynos-acpm: fix reading longer results dt-bindings: firmware: google,gs101-acpm-ipc: add PMIC child node firmware: exynos-acpm: allow use during system shutdown firmware: exynos-acpm: use ktime APIs for timeout detection Link: https://lore.kernel.org/r/20250501103541.13795-2-krzysztof.kozlowski@linaro.org Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
@@ -27,6 +27,15 @@ properties:
|
||||
mboxes:
|
||||
maxItems: 1
|
||||
|
||||
pmic:
|
||||
description: Child node describing the main PMIC.
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: samsung,s2mpg10-pmic
|
||||
|
||||
shmem:
|
||||
description:
|
||||
List of phandle pointing to the shared memory (SHM) area. The memory
|
||||
@@ -43,8 +52,34 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
power-management {
|
||||
compatible = "google,gs101-acpm-ipc";
|
||||
mboxes = <&ap2apm_mailbox>;
|
||||
shmem = <&apm_sram>;
|
||||
|
||||
pmic {
|
||||
compatible = "samsung,s2mpg10-pmic";
|
||||
interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulators {
|
||||
LDO1 {
|
||||
regulator-name = "vdd_ldo1";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
BUCK1 {
|
||||
regulator-name = "vdd_mif";
|
||||
regulator-min-microvolt = <450000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -43,13 +43,13 @@ static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)
|
||||
return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;
|
||||
}
|
||||
|
||||
static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd,
|
||||
static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,
|
||||
unsigned int acpm_chan_id)
|
||||
{
|
||||
xfer->txd = cmd;
|
||||
xfer->rxd = cmd;
|
||||
xfer->txlen = sizeof(cmd);
|
||||
xfer->rxlen = sizeof(cmd);
|
||||
xfer->txlen = cmdlen;
|
||||
xfer->rxlen = cmdlen;
|
||||
xfer->acpm_chan_id = acpm_chan_id;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ int acpm_pmic_read_reg(const struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_read_cmd(cmd, type, reg, chan);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -104,7 +104,7 @@ int acpm_pmic_bulk_read(const struct acpm_handle *handle,
|
||||
return -EINVAL;
|
||||
|
||||
acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -144,7 +144,7 @@ int acpm_pmic_write_reg(const struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -184,7 +184,7 @@ int acpm_pmic_bulk_write(const struct acpm_handle *handle,
|
||||
return -EINVAL;
|
||||
|
||||
acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
@@ -214,7 +214,7 @@ int acpm_pmic_update_reg(const struct acpm_handle *handle,
|
||||
int ret;
|
||||
|
||||
acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id);
|
||||
acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);
|
||||
|
||||
ret = acpm_do_xfer(handle, &xfer);
|
||||
if (ret)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/mailbox/exynos-message.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
@@ -32,8 +33,7 @@
|
||||
|
||||
#define ACPM_PROTOCOL_SEQNUM GENMASK(21, 16)
|
||||
|
||||
/* The unit of counter is 20 us. 5000 * 20 = 100 ms */
|
||||
#define ACPM_POLL_TIMEOUT 5000
|
||||
#define ACPM_POLL_TIMEOUT_US (100 * USEC_PER_MSEC)
|
||||
#define ACPM_TX_TIMEOUT_US 500000
|
||||
|
||||
#define ACPM_GS101_INITDATA_BASE 0xa000
|
||||
@@ -284,12 +284,13 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan,
|
||||
const struct acpm_xfer *xfer)
|
||||
{
|
||||
struct device *dev = achan->acpm->dev;
|
||||
unsigned int cnt_20us = 0;
|
||||
ktime_t timeout;
|
||||
u32 seqnum;
|
||||
int ret;
|
||||
|
||||
seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]);
|
||||
|
||||
timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US);
|
||||
do {
|
||||
ret = acpm_get_rx(achan, xfer);
|
||||
if (ret)
|
||||
@@ -299,12 +300,11 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan,
|
||||
return 0;
|
||||
|
||||
/* Determined experimentally. */
|
||||
usleep_range(20, 30);
|
||||
cnt_20us++;
|
||||
} while (cnt_20us < ACPM_POLL_TIMEOUT);
|
||||
udelay(20);
|
||||
} while (ktime_before(ktime_get(), timeout));
|
||||
|
||||
dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx, cnt_20us = %d.\n",
|
||||
achan->id, seqnum, achan->bitmap_seqnum[0], cnt_20us);
|
||||
dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx.\n",
|
||||
achan->id, seqnum, achan->bitmap_seqnum[0]);
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
@@ -633,7 +633,7 @@ static int acpm_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, acpm);
|
||||
|
||||
return 0;
|
||||
return devm_of_platform_populate(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,43 +661,30 @@ static void devm_acpm_release(struct device *dev, void *res)
|
||||
}
|
||||
|
||||
/**
|
||||
* acpm_get_by_phandle() - get the ACPM handle using DT phandle.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @property: property name containing phandle on ACPM node.
|
||||
* acpm_get_by_node() - get the ACPM handle using node pointer.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @np: ACPM device tree node.
|
||||
*
|
||||
* Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
|
||||
*/
|
||||
static const struct acpm_handle *acpm_get_by_phandle(struct device *dev,
|
||||
const char *property)
|
||||
static const struct acpm_handle *acpm_get_by_node(struct device *dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *acpm_np;
|
||||
struct device_link *link;
|
||||
struct acpm_info *acpm;
|
||||
|
||||
acpm_np = of_parse_phandle(dev->of_node, property, 0);
|
||||
if (!acpm_np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pdev = of_find_device_by_node(acpm_np);
|
||||
if (!pdev) {
|
||||
dev_err(dev, "Cannot find device node %s\n", acpm_np->name);
|
||||
of_node_put(acpm_np);
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
of_node_put(acpm_np);
|
||||
|
||||
acpm = platform_get_drvdata(pdev);
|
||||
if (!acpm) {
|
||||
dev_err(dev, "Cannot get drvdata from %s\n",
|
||||
dev_name(&pdev->dev));
|
||||
platform_device_put(pdev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
if (!try_module_get(pdev->dev.driver->owner)) {
|
||||
dev_err(dev, "Cannot get module reference.\n");
|
||||
platform_device_put(pdev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
@@ -716,14 +703,14 @@ static const struct acpm_handle *acpm_get_by_phandle(struct device *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_acpm_get_by_phandle() - managed get handle using phandle.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @property: property name containing phandle on ACPM node.
|
||||
* devm_acpm_get_by_node() - managed get handle using node pointer.
|
||||
* @dev: device pointer requesting ACPM handle.
|
||||
* @np: ACPM device tree node.
|
||||
*
|
||||
* Return: pointer to handle on success, ERR_PTR(-errno) otherwise.
|
||||
*/
|
||||
const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
const char *property)
|
||||
const struct acpm_handle *devm_acpm_get_by_node(struct device *dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
const struct acpm_handle **ptr, *handle;
|
||||
|
||||
@@ -731,7 +718,7 @@ const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
handle = acpm_get_by_phandle(dev, property);
|
||||
handle = acpm_get_by_node(dev, np);
|
||||
if (!IS_ERR(handle)) {
|
||||
*ptr = handle;
|
||||
devres_add(dev, ptr);
|
||||
@@ -741,6 +728,7 @@ const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
|
||||
return handle;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_acpm_get_by_node);
|
||||
|
||||
static const struct acpm_match_data acpm_gs101 = {
|
||||
.initdata_base = ACPM_GS101_INITDATA_BASE,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
struct acpm_handle;
|
||||
struct device_node;
|
||||
|
||||
struct acpm_pmic_ops {
|
||||
int (*read_reg)(const struct acpm_handle *handle,
|
||||
@@ -44,6 +45,7 @@ struct acpm_handle {
|
||||
|
||||
struct device;
|
||||
|
||||
const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev,
|
||||
const char *property);
|
||||
const struct acpm_handle *devm_acpm_get_by_node(struct device *dev,
|
||||
struct device_node *np);
|
||||
|
||||
#endif /* __EXYNOS_ACPM_PROTOCOL_H */
|
||||
|
||||
Reference in New Issue
Block a user