Loading drivers/soc/qcom/cx_ipeak.c +184 −2 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include <linux/module.h> Loading @@ -12,6 +12,7 @@ #include <linux/of_device.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/interrupt.h> #include <soc/qcom/cx_ipeak.h> Loading @@ -29,9 +30,13 @@ #define CXIP_CLIENT_OFFSET 0x1000 #define CXIP_CLIENT10_OFFSET 0x3000 #define CXIP_VICTIM_OFFSET 0xB000 #define CXIP_POLL_TIMEOUT_US (50 * 1000) #define CXIP_VICTIMS 3 #define VICTIM_ENTRIES 3 struct cx_ipeak_client; struct cx_ipeak_core_ops { Loading @@ -43,14 +48,27 @@ static struct cx_ipeak_device { spinlock_t vote_lock; void __iomem *tcsr_vptr; struct cx_ipeak_core_ops *core_ops; u32 victims_count; u32 victims_reg_count; u32 danger_intr_num; u32 safe_intr_num; } device_ipeak; struct cx_ipeak_client { int vote_count; unsigned int offset; int client_id; struct cx_ipeak_device *dev; }; static struct cx_ipeak_victims { int client_id; int victim_id; u32 freq_limit; void *data; cx_ipeak_victim_fn victim_cb; } victims_ipeak[CXIP_VICTIMS]; /** * cx_ipeak_register() - allocate client structure and fill device private and * offset details. Loading Loading @@ -84,6 +102,9 @@ struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node, if (device_ipeak.core_ops) client = device_ipeak.core_ops->register_client (cx_spec.args[0]); client->client_id = cx_spec.args[0]; return client; } EXPORT_SYMBOL(cx_ipeak_register); Loading Loading @@ -145,7 +166,57 @@ static struct cx_ipeak_client *cx_ipeak_register_v2(int client_id) return client; } /* /** * cx_ipeak_victim_register - victim registration API to handle * the cx ipeak hw interrupts (danger/safe) to throttle freq. * @client: cx ipeak client * @victim_cb: callback function of victim * @data: data to be passed to victim while handling irq */ int cx_ipeak_victim_register(struct cx_ipeak_client *client, cx_ipeak_victim_fn victim_cb, void *data) { int i = 0; if (!victim_cb) return -EINVAL; while (i < device_ipeak.victims_count) { if (client->client_id == victims_ipeak[i].client_id) { victims_ipeak[i].victim_cb = victim_cb; victims_ipeak[i].data = data; device_ipeak.victims_reg_count++; break; } i++; } return 0; } EXPORT_SYMBOL(cx_ipeak_victim_register); /** * cx_ipeak_victim_unregister - unregister victim client from * cx_ipeak driver. * @client: cx ipeak client */ void cx_ipeak_victim_unregister(struct cx_ipeak_client *client) { int i = 0; while (i < device_ipeak.victims_count) { if (client->client_id == victims_ipeak[i].client_id) { victims_ipeak[i].victim_cb = NULL; victims_ipeak[i].data = NULL; device_ipeak.victims_reg_count--; break; } i++; } } EXPORT_SYMBOL(cx_ipeak_victim_unregister); /** * cx_ipeak_update() - Set/Clear client vote for Cx iPeak limit * manager to throttle cDSP. * @client: client handle. Loading Loading @@ -258,6 +329,61 @@ static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote) return ret; } static irqreturn_t cx_ipeak_irq_handler(int irq, void *data) { int i; irqreturn_t ret = IRQ_NONE; for (i = 0; i < device_ipeak.victims_reg_count; i++) { cx_ipeak_victim_fn victim_cb = victims_ipeak[i].victim_cb; if (irq == device_ipeak.danger_intr_num) { /* * To set frequency limit at victim client * side in danger interrupt case */ victim_cb(victims_ipeak[i].data, victims_ipeak[i].freq_limit); writel_relaxed(1, (device_ipeak.tcsr_vptr + CXIP_VICTIM_OFFSET + ((victims_ipeak[i].victim_id)* CXIP_CLIENT_OFFSET))); ret = IRQ_HANDLED; } else if (irq == device_ipeak.safe_intr_num) { /* * To remove frequency limit at victim client * side in safe interrupt case */ victim_cb(victims_ipeak[i].data, 0); writel_relaxed(0, (device_ipeak.tcsr_vptr + CXIP_VICTIM_OFFSET + ((victims_ipeak[i].victim_id)* CXIP_CLIENT_OFFSET))); ret = IRQ_HANDLED; } } return ret; } int cx_ipeak_request_irq(struct platform_device *pdev, const char *name, irq_handler_t handler, void *data) { int ret, num = platform_get_irq_byname(pdev, name); if (num < 0) return num; ret = devm_request_irq(&pdev->dev, num, handler, IRQF_TRIGGER_RISING, name, data); if (ret) dev_err(&pdev->dev, "Unable to get interrupt %s: %d\n", name, ret); return ret ? ret : num; } /** * cx_ipeak_unregister() - unregister client * @client: client address to free Loading @@ -283,6 +409,9 @@ struct cx_ipeak_core_ops core_ops_v2 = { static int cx_ipeak_probe(struct platform_device *pdev) { struct resource *res; int status = -EINVAL; int i, ret, count; u32 victim_en; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); device_ipeak.tcsr_vptr = devm_ioremap_resource(&pdev->dev, res); Loading @@ -297,6 +426,59 @@ static int cx_ipeak_probe(struct platform_device *pdev) else device_ipeak.core_ops = NULL; victim_en = of_property_read_bool(pdev->dev.of_node, "victims_table"); if (victim_en) { count = of_property_count_u32_elems(pdev->dev.of_node, "victims_table"); if (((count%VICTIM_ENTRIES) != 0) || ((count/VICTIM_ENTRIES) > CXIP_VICTIMS)) return -EINVAL; for (i = 0; i < (count/VICTIM_ENTRIES); i++) { ret = of_property_read_u32_index(pdev->dev.of_node, "victims_table", i*VICTIM_ENTRIES, &victims_ipeak[i].client_id); if (ret) return ret; ret = of_property_read_u32_index(pdev->dev.of_node, "victims_table", (i*VICTIM_ENTRIES) + 1, &victims_ipeak[i].victim_id); if (ret) return ret; ret = of_property_read_u32_index(pdev->dev.of_node, "victims_table", (i*VICTIM_ENTRIES) + 2, &victims_ipeak[i].freq_limit); if (ret) return ret; device_ipeak.victims_count++; } status = cx_ipeak_request_irq(pdev, "cx_ipeak_danger", cx_ipeak_irq_handler, NULL); if (status < 0) return status; device_ipeak.danger_intr_num = status; status = cx_ipeak_request_irq(pdev, "cx_ipeak_safe", cx_ipeak_irq_handler, NULL); if (status < 0) return status; device_ipeak.safe_intr_num = status; } spin_lock_init(&device_ipeak.vote_lock); return 0; } Loading include/soc/qcom/cx_ipeak.h +16 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. * Copyright (c) 2018, 2020 The Linux Foundation. All rights reserved. */ #ifndef __SOC_COM_CX_IPEAK_H #define __SOC_COM_CX_IPEAK_H typedef void (*cx_ipeak_victim_fn)(void *data, u32 freq_limit); struct device_node; struct cx_ipeak_client; Loading @@ -27,12 +29,25 @@ static inline int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, { return 0; } static inline int cx_ipeak_victim_register(struct cx_ipeak_client *client, cx_ipeak_victim_fn victim_cb, void *data) { return 0; } static inline void cx_ipeak_victim_unregister(struct cx_ipeak_client *client) { } #else struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node, const char *client_name); void cx_ipeak_unregister(struct cx_ipeak_client *client); int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, bool vote); int cx_ipeak_victim_register(struct cx_ipeak_client *client, cx_ipeak_victim_fn victim_cb, void *data); void cx_ipeak_victim_unregister(struct cx_ipeak_client *client); #endif Loading Loading
drivers/soc/qcom/cx_ipeak.c +184 −2 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include <linux/module.h> Loading @@ -12,6 +12,7 @@ #include <linux/of_device.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/interrupt.h> #include <soc/qcom/cx_ipeak.h> Loading @@ -29,9 +30,13 @@ #define CXIP_CLIENT_OFFSET 0x1000 #define CXIP_CLIENT10_OFFSET 0x3000 #define CXIP_VICTIM_OFFSET 0xB000 #define CXIP_POLL_TIMEOUT_US (50 * 1000) #define CXIP_VICTIMS 3 #define VICTIM_ENTRIES 3 struct cx_ipeak_client; struct cx_ipeak_core_ops { Loading @@ -43,14 +48,27 @@ static struct cx_ipeak_device { spinlock_t vote_lock; void __iomem *tcsr_vptr; struct cx_ipeak_core_ops *core_ops; u32 victims_count; u32 victims_reg_count; u32 danger_intr_num; u32 safe_intr_num; } device_ipeak; struct cx_ipeak_client { int vote_count; unsigned int offset; int client_id; struct cx_ipeak_device *dev; }; static struct cx_ipeak_victims { int client_id; int victim_id; u32 freq_limit; void *data; cx_ipeak_victim_fn victim_cb; } victims_ipeak[CXIP_VICTIMS]; /** * cx_ipeak_register() - allocate client structure and fill device private and * offset details. Loading Loading @@ -84,6 +102,9 @@ struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node, if (device_ipeak.core_ops) client = device_ipeak.core_ops->register_client (cx_spec.args[0]); client->client_id = cx_spec.args[0]; return client; } EXPORT_SYMBOL(cx_ipeak_register); Loading Loading @@ -145,7 +166,57 @@ static struct cx_ipeak_client *cx_ipeak_register_v2(int client_id) return client; } /* /** * cx_ipeak_victim_register - victim registration API to handle * the cx ipeak hw interrupts (danger/safe) to throttle freq. * @client: cx ipeak client * @victim_cb: callback function of victim * @data: data to be passed to victim while handling irq */ int cx_ipeak_victim_register(struct cx_ipeak_client *client, cx_ipeak_victim_fn victim_cb, void *data) { int i = 0; if (!victim_cb) return -EINVAL; while (i < device_ipeak.victims_count) { if (client->client_id == victims_ipeak[i].client_id) { victims_ipeak[i].victim_cb = victim_cb; victims_ipeak[i].data = data; device_ipeak.victims_reg_count++; break; } i++; } return 0; } EXPORT_SYMBOL(cx_ipeak_victim_register); /** * cx_ipeak_victim_unregister - unregister victim client from * cx_ipeak driver. * @client: cx ipeak client */ void cx_ipeak_victim_unregister(struct cx_ipeak_client *client) { int i = 0; while (i < device_ipeak.victims_count) { if (client->client_id == victims_ipeak[i].client_id) { victims_ipeak[i].victim_cb = NULL; victims_ipeak[i].data = NULL; device_ipeak.victims_reg_count--; break; } i++; } } EXPORT_SYMBOL(cx_ipeak_victim_unregister); /** * cx_ipeak_update() - Set/Clear client vote for Cx iPeak limit * manager to throttle cDSP. * @client: client handle. Loading Loading @@ -258,6 +329,61 @@ static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote) return ret; } static irqreturn_t cx_ipeak_irq_handler(int irq, void *data) { int i; irqreturn_t ret = IRQ_NONE; for (i = 0; i < device_ipeak.victims_reg_count; i++) { cx_ipeak_victim_fn victim_cb = victims_ipeak[i].victim_cb; if (irq == device_ipeak.danger_intr_num) { /* * To set frequency limit at victim client * side in danger interrupt case */ victim_cb(victims_ipeak[i].data, victims_ipeak[i].freq_limit); writel_relaxed(1, (device_ipeak.tcsr_vptr + CXIP_VICTIM_OFFSET + ((victims_ipeak[i].victim_id)* CXIP_CLIENT_OFFSET))); ret = IRQ_HANDLED; } else if (irq == device_ipeak.safe_intr_num) { /* * To remove frequency limit at victim client * side in safe interrupt case */ victim_cb(victims_ipeak[i].data, 0); writel_relaxed(0, (device_ipeak.tcsr_vptr + CXIP_VICTIM_OFFSET + ((victims_ipeak[i].victim_id)* CXIP_CLIENT_OFFSET))); ret = IRQ_HANDLED; } } return ret; } int cx_ipeak_request_irq(struct platform_device *pdev, const char *name, irq_handler_t handler, void *data) { int ret, num = platform_get_irq_byname(pdev, name); if (num < 0) return num; ret = devm_request_irq(&pdev->dev, num, handler, IRQF_TRIGGER_RISING, name, data); if (ret) dev_err(&pdev->dev, "Unable to get interrupt %s: %d\n", name, ret); return ret ? ret : num; } /** * cx_ipeak_unregister() - unregister client * @client: client address to free Loading @@ -283,6 +409,9 @@ struct cx_ipeak_core_ops core_ops_v2 = { static int cx_ipeak_probe(struct platform_device *pdev) { struct resource *res; int status = -EINVAL; int i, ret, count; u32 victim_en; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); device_ipeak.tcsr_vptr = devm_ioremap_resource(&pdev->dev, res); Loading @@ -297,6 +426,59 @@ static int cx_ipeak_probe(struct platform_device *pdev) else device_ipeak.core_ops = NULL; victim_en = of_property_read_bool(pdev->dev.of_node, "victims_table"); if (victim_en) { count = of_property_count_u32_elems(pdev->dev.of_node, "victims_table"); if (((count%VICTIM_ENTRIES) != 0) || ((count/VICTIM_ENTRIES) > CXIP_VICTIMS)) return -EINVAL; for (i = 0; i < (count/VICTIM_ENTRIES); i++) { ret = of_property_read_u32_index(pdev->dev.of_node, "victims_table", i*VICTIM_ENTRIES, &victims_ipeak[i].client_id); if (ret) return ret; ret = of_property_read_u32_index(pdev->dev.of_node, "victims_table", (i*VICTIM_ENTRIES) + 1, &victims_ipeak[i].victim_id); if (ret) return ret; ret = of_property_read_u32_index(pdev->dev.of_node, "victims_table", (i*VICTIM_ENTRIES) + 2, &victims_ipeak[i].freq_limit); if (ret) return ret; device_ipeak.victims_count++; } status = cx_ipeak_request_irq(pdev, "cx_ipeak_danger", cx_ipeak_irq_handler, NULL); if (status < 0) return status; device_ipeak.danger_intr_num = status; status = cx_ipeak_request_irq(pdev, "cx_ipeak_safe", cx_ipeak_irq_handler, NULL); if (status < 0) return status; device_ipeak.safe_intr_num = status; } spin_lock_init(&device_ipeak.vote_lock); return 0; } Loading
include/soc/qcom/cx_ipeak.h +16 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. * Copyright (c) 2018, 2020 The Linux Foundation. All rights reserved. */ #ifndef __SOC_COM_CX_IPEAK_H #define __SOC_COM_CX_IPEAK_H typedef void (*cx_ipeak_victim_fn)(void *data, u32 freq_limit); struct device_node; struct cx_ipeak_client; Loading @@ -27,12 +29,25 @@ static inline int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, { return 0; } static inline int cx_ipeak_victim_register(struct cx_ipeak_client *client, cx_ipeak_victim_fn victim_cb, void *data) { return 0; } static inline void cx_ipeak_victim_unregister(struct cx_ipeak_client *client) { } #else struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node, const char *client_name); void cx_ipeak_unregister(struct cx_ipeak_client *client); int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, bool vote); int cx_ipeak_victim_register(struct cx_ipeak_client *client, cx_ipeak_victim_fn victim_cb, void *data); void cx_ipeak_victim_unregister(struct cx_ipeak_client *client); #endif Loading