* [PATCH] i2c: core: Use GPIO descriptors for recovery
From: Linus Walleij @ 2017-12-08 10:20 UTC (permalink / raw)
To: linux-arm-kernel
The I2C core contains code for handling a fallback recovery
using GPIO when a piece of I2C hardware fails in duty.
This code passes GPIO numbers from the global GPIO
numberspace around and we want to get rid of this and use
GPIO descriptors associated with the devices.
After some digging it turns out that there are two users
of the mechanism in the entire kernel:
- i.MX that simply define "scl-gpios" and "sda-gpios" in
the device tree which is already creating descriptors
associated with the device, that we can pick up and use.
- Two DaVinci boards that pass the SCL and SDA GPIOs using
custom platform data.
Refactor the core to grab the SCL and SDA GPIO descriptors
on-demand when doing recovery, and release them when
recovery is done. Grab the named descriptors "scl" and
"sda" which will make the i.MX method work out of the
box.
Augment the two DaVinci board files to pass descriptors
using static tables.
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Sekhar Nori <nsekhar@ti.com>
Cc: Kevin Hilman <khilman@kernel.org>
Cc: Keerthy <j-keerthy@ti.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
It would be great to have this tested on i.MX and
DaVinci, or at least ACKed by the maintainers.
---
arch/arm/mach-davinci/board-dm355-evm.c | 15 +++++++--
arch/arm/mach-davinci/board-dm644x-evm.c | 15 +++++++--
drivers/i2c/busses/i2c-davinci.c | 4 +--
drivers/i2c/busses/i2c-imx.c | 12 ++------
drivers/i2c/i2c-core-base.c | 51 +++++++++++++------------------
include/linux/i2c.h | 9 +++---
include/linux/platform_data/i2c-davinci.h | 5 ++-
7 files changed, 58 insertions(+), 53 deletions(-)
diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c
index 62e7bc3018f0..60aec5437276 100644
--- a/arch/arm/mach-davinci/board-dm355-evm.c
+++ b/arch/arm/mach-davinci/board-dm355-evm.c
@@ -17,6 +17,7 @@
#include <linux/mtd/rawnand.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
#include <linux/clk.h>
#include <linux/videodev2.h>
#include <media/i2c/tvp514x.h>
@@ -108,11 +109,20 @@ static struct platform_device davinci_nand_device = {
},
};
+static struct gpiod_lookup_table i2c_recovery_gpiod_table = {
+ .dev_id = "i2c_davinci",
+ .table = {
+ GPIO_LOOKUP("davinci_gpio.0", 15, "sda",
+ GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
+ GPIO_LOOKUP("davinci_gpio.0", 14, "scl",
+ GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
+ },
+};
+
static struct davinci_i2c_platform_data i2c_pdata = {
.bus_freq = 400 /* kHz */,
.bus_delay = 0 /* usec */,
- .sda_pin = 15,
- .scl_pin = 14,
+ .gpio_recovery = true,
};
static int dm355evm_mmc_gpios = -EINVAL;
@@ -141,6 +151,7 @@ static struct i2c_board_info dm355evm_i2c_info[] = {
static void __init evm_init_i2c(void)
{
+ gpiod_add_lookup_table(&i2c_recovery_gpiod_table);
davinci_init_i2c(&i2c_pdata);
gpio_request(5, "dm355evm_msp");
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index b07c9b18d427..2efc6dbc5ac0 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -13,6 +13,7 @@
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
#include <linux/i2c.h>
#include <linux/platform_data/pcf857x.h>
#include <linux/platform_data/at24.h>
@@ -595,18 +596,28 @@ static struct i2c_board_info __initdata i2c_info[] = {
},
};
+static struct gpiod_lookup_table i2c_recovery_gpiod_table = {
+ .dev_id = "i2c_davinci",
+ .table = {
+ GPIO_LOOKUP("davinci_gpio.0", 44, "sda",
+ GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
+ GPIO_LOOKUP("davinci_gpio.0", 43, "scl",
+ GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
+ },
+};
+
/* The msp430 uses a slow bitbanged I2C implementation (ergo 20 KHz),
* which requires 100 usec of idle bus after i2c writes sent to it.
*/
static struct davinci_i2c_platform_data i2c_pdata = {
.bus_freq = 20 /* kHz */,
.bus_delay = 100 /* usec */,
- .sda_pin = 44,
- .scl_pin = 43,
+ .gpio_recovery = true,
};
static void __init evm_init_i2c(void)
{
+ gpiod_add_lookup_table(&i2c_recovery_gpiod_table);
davinci_init_i2c(&i2c_pdata);
i2c_add_driver(&dm6446evm_msp_driver);
i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info));
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 2ead9b9eebb7..5fae5daba946 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -868,10 +868,8 @@ static int davinci_i2c_probe(struct platform_device *pdev)
if (dev->pdata->has_pfunc)
adap->bus_recovery_info = &davinci_i2c_scl_recovery_info;
- else if (dev->pdata->scl_pin) {
+ else if (dev->pdata->gpio_recovery) {
adap->bus_recovery_info = &davinci_i2c_gpio_recovery_info;
- adap->bus_recovery_info->scl_gpio = dev->pdata->scl_pin;
- adap->bus_recovery_info->sda_gpio = dev->pdata->sda_pin;
}
adap->nr = pdev->id;
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index f96830ffd9f1..f2e47ba2922f 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -46,7 +46,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
-#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/i2c-imx.h>
#include <linux/platform_device.h>
@@ -1006,15 +1005,8 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
PINCTRL_STATE_DEFAULT);
i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
"gpio");
- rinfo->sda_gpio = of_get_named_gpio(pdev->dev.of_node, "sda-gpios", 0);
- rinfo->scl_gpio = of_get_named_gpio(pdev->dev.of_node, "scl-gpios", 0);
-
- if (rinfo->sda_gpio == -EPROBE_DEFER ||
- rinfo->scl_gpio == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (!gpio_is_valid(rinfo->sda_gpio) ||
- !gpio_is_valid(rinfo->scl_gpio) ||
- IS_ERR(i2c_imx->pinctrl_pins_default) ||
+
+ if (IS_ERR(i2c_imx->pinctrl_pins_default) ||
IS_ERR(i2c_imx->pinctrl_pins_gpio)) {
dev_dbg(&pdev->dev, "recovery information incomplete\n");
return 0;
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 706164b4c5be..004bb83d6705 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -27,7 +27,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/idr.h>
@@ -134,42 +134,45 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
/* i2c bus recovery routines */
static int get_scl_gpio_value(struct i2c_adapter *adap)
{
- return gpio_get_value(adap->bus_recovery_info->scl_gpio);
+ return gpiod_get_value(adap->bus_recovery_info->scl_gpio);
}
static void set_scl_gpio_value(struct i2c_adapter *adap, int val)
{
- gpio_set_value(adap->bus_recovery_info->scl_gpio, val);
+ gpiod_set_value(adap->bus_recovery_info->scl_gpio, val);
}
static int get_sda_gpio_value(struct i2c_adapter *adap)
{
- return gpio_get_value(adap->bus_recovery_info->sda_gpio);
+ return gpiod_get_value(adap->bus_recovery_info->sda_gpio);
}
static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
struct device *dev = &adap->dev;
- int ret = 0;
+ int ret;
- ret = gpio_request_one(bri->scl_gpio, GPIOF_OPEN_DRAIN |
- GPIOF_OUT_INIT_HIGH, "i2c-scl");
- if (ret) {
- dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio);
- return ret;
+ bri->scl_gpio = gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN);
+ if (IS_ERR(bri->scl_gpio)) {
+ dev_warn(dev, "Can't get SCL gpio\n");
+ return PTR_ERR(bri->scl_gpio);
}
- if (bri->get_sda) {
- if (gpio_request_one(bri->sda_gpio, GPIOF_IN, "i2c-sda")) {
- /* work without SDA polling */
- dev_warn(dev, "Can't get SDA gpio: %d. Not using SDA polling\n",
- bri->sda_gpio);
- bri->get_sda = NULL;
+ bri->sda_gpio = gpiod_get(dev, "sda", GPIOD_IN);
+ if (IS_ERR(bri->sda_gpio)) {
+ /* Work without SDA polling */
+ bri->get_sda = NULL;
+ ret = PTR_ERR(bri->sda_gpio);
+ if (ret != -ENOENT) {
+ dev_warn(dev,
+ "Can't get SDA gpio. Not using SDA polling\n");
}
+ } else {
+ bri->get_sda = get_sda_gpio_value;
}
- return ret;
+ return 0;
}
static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap)
@@ -177,9 +180,9 @@ static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap)
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
if (bri->get_sda)
- gpio_free(bri->sda_gpio);
+ gpiod_put(bri->sda_gpio);
- gpio_free(bri->scl_gpio);
+ gpiod_put(bri->scl_gpio);
}
/*
@@ -279,16 +282,6 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
/* Generic GPIO recovery */
if (bri->recover_bus == i2c_generic_gpio_recovery) {
- if (!gpio_is_valid(bri->scl_gpio)) {
- err_str = "invalid SCL gpio";
- goto err;
- }
-
- if (gpio_is_valid(bri->sda_gpio))
- bri->get_sda = get_sda_gpio_value;
- else
- bri->get_sda = NULL;
-
bri->get_scl = get_scl_gpio_value;
bri->set_scl = set_scl_gpio_value;
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 0f774406fad0..e1549be3aa42 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -34,6 +34,7 @@
#include <linux/irqdomain.h> /* for Host Notify IRQ */
#include <linux/of.h> /* for struct device_node */
#include <linux/swab.h> /* for swab16 */
+#include <linux/gpio/consumer.h>
#include <uapi/linux/i2c.h>
extern struct bus_type i2c_bus_type;
@@ -497,8 +498,8 @@ struct i2c_timings {
* configure padmux here for SDA/SCL line or something else they want.
* @unprepare_recovery: This will be called after completing recovery. Platform
* may configure padmux here for SDA/SCL line or something else they want.
- * @scl_gpio: gpio number of the SCL line. Only required for GPIO recovery.
- * @sda_gpio: gpio number of the SDA line. Only required for GPIO recovery.
+ * @scl_gpio: gpio descriptor for the SCL line. Optional for GPIO recovery.
+ * @sda_gpio: gpio descriptor for the SDA line. Optional for GPIO recovery.
*/
struct i2c_bus_recovery_info {
int (*recover_bus)(struct i2c_adapter *);
@@ -511,8 +512,8 @@ struct i2c_bus_recovery_info {
void (*unprepare_recovery)(struct i2c_adapter *);
/* gpio recovery */
- int scl_gpio;
- int sda_gpio;
+ struct gpio_desc *scl_gpio;
+ struct gpio_desc *sda_gpio;
};
int i2c_recover_bus(struct i2c_adapter *adap);
diff --git a/include/linux/platform_data/i2c-davinci.h b/include/linux/platform_data/i2c-davinci.h
index 89fd34727a24..98967df07468 100644
--- a/include/linux/platform_data/i2c-davinci.h
+++ b/include/linux/platform_data/i2c-davinci.h
@@ -16,9 +16,8 @@
struct davinci_i2c_platform_data {
unsigned int bus_freq; /* standard bus frequency (kHz) */
unsigned int bus_delay; /* post-transaction delay (usec) */
- unsigned int sda_pin; /* GPIO pin ID to use for SDA */
- unsigned int scl_pin; /* GPIO pin ID to use for SCL */
- bool has_pfunc; /*chip has a ICPFUNC register */
+ bool gpio_recovery; /* Use GPIO recovery method */
+ bool has_pfunc; /* Chip has a ICPFUNC register */
};
/* for board setup code */
--
2.14.3
^ permalink raw reply related
* [PATCH] rcutorture: Add basic ARM64 support to run scripts
From: lianglihao at huawei.com @ 2017-12-08 10:13 UTC (permalink / raw)
To: linux-arm-kernel
From: Lihao Liang <lianglihao@huawei.com>
This commit adds support of the qemu command qemu-system-aarch64
to rcutorture. Use the following command to run:
./kvm.sh --qemu-cmd qemu-system-aarch64
Signed-off-by: Lihao Liang <lianglihao@huawei.com>
---
The max CPUs supported by qemu machine 'virt' is 8 so the value of
CONFIG_NR_CPUS in some test configuration files needs to be adjusted.
tools/testing/selftests/rcutorture/bin/functions.sh | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh
index 07a1377..5ffe4fe 100644
--- a/tools/testing/selftests/rcutorture/bin/functions.sh
+++ b/tools/testing/selftests/rcutorture/bin/functions.sh
@@ -136,6 +136,9 @@ identify_boot_image () {
qemu-system-x86_64|qemu-system-i386)
echo arch/x86/boot/bzImage
;;
+ qemu-system-aarch64)
+ echo arch/arm64/boot/Image
+ ;;
*)
echo vmlinux
;;
@@ -185,7 +188,14 @@ identify_qemu_append () {
then
echo root=/dev/sda
else
- echo console=ttyS0
+ case "$1" in
+ qemu-system-aarch64)
+ echo console=ttyAMA0
+ ;;
+ *)
+ echo console=ttyS0
+ ;;
+ esac
fi
}
@@ -197,6 +207,9 @@ identify_qemu_args () {
case "$1" in
qemu-system-x86_64|qemu-system-i386)
;;
+ qemu-system-aarch64)
+ echo -M virt -cpu host
+ ;;
qemu-system-ppc64)
echo -enable-kvm -M pseries -nodefaults
echo -device spapr-vscsi
@@ -257,6 +270,9 @@ specify_qemu_cpus () {
qemu-system-x86_64|qemu-system-i386)
echo $2 -smp $3
;;
+ qemu-system-aarch64)
+ echo $2 -smp $3
+ ;;
qemu-system-ppc64)
nt="`lscpu | grep '^NUMA node0' | sed -e 's/^[^,]*,\([0-9]*\),.*$/\1/'`"
echo $2 -smp cores=`expr \( $3 + $nt - 1 \) / $nt`,threads=$nt
--
2.7.4
^ permalink raw reply related
* [RFC v3 PATCH 0/2] Introduce Security Version to EFI Stub
From: Gary Lin @ 2017-12-08 10:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171207142657.52e1363a@alans-desktop>
On Thu, Dec 07, 2017 at 02:26:57PM +0000, Alan Cox wrote:
> On Tue, 5 Dec 2017 18:01:46 +0800
> Gary Lin <glin@suse.com> wrote:
>
> > The series of patches introduce Security Version to EFI stub.
> >
> > Security Version is a monotonically increasing number and designed to
> > prevent the user from loading an insecure kernel accidentally. The
> > bootloader maintains a list of security versions corresponding to
> > different distributions. After fixing a critical vulnerability, the
> > distribution kernel maintainer bumps the "version", and the bootloader
> > updates the list automatically.
>
> This seems a mindbogglingly complicated way to implement something you
> could do with a trivial script in the package that updates the list of
> iffy kernels and when generating the new grub.conf puts them in a menu
> of 'old insecure' kernels.
>
Ya, a menu for those kernels is also a kind of warning to the users.
Thanks for pointing the direction.
> Why do you even need this in the EFI stub ?
>
For 2 reasons.
1. We want the version number being unalterable. In a system with UEFI
Secure Boot, the signature appended to the kernel guarantees that the
version number is genuine.
2. For the cross-architecture support. Since the version number could be
anywhere in the image file, we need at least a offset to point to the
version number. That's why I choose the resource section/table in the
EFI header. It exists as long as the EFI stub exists. The design of
the directory allows different data stored separately.
If there is a better place for the version number, I'd be glad to adopt
it.
> What happens if you want to invalidate an old kernel but not push a new
> one ? Today if you've got a package that maintains the list of 'iffy'
> kernels you can push a tiny package, under your scheme you've got to push
> new kernels which is an un-necessary and high risk OS change.
>
The idea is that the security version only bumps only when it's really
necessary. For example, the new kernel fixes a severe bug that invalidates
the signature check of the modules. Or, the distro makes an official
release and would like to obsolete the alpha/beta/RC kernel. Anyway, it
depends on the distro policy.
Actually, our main target is only for the enterprise server, and the
kernel update path is relatively simple. Since it may bring some
confusion in a multi-boot system, we are currently not planning to
enable SV for the community distro, i.e. openSUSE.
Thanks,
Gary Lin
^ permalink raw reply
* [RESEND PATCH 0/4] drm/meson: power domain init related fixes
From: Neil Armstrong @ 2017-12-08 9:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512723860.17256.12.camel@baylibre.com>
On 08/12/2017 10:04, Jerome Brunet wrote:
> On Wed, 2017-12-06 at 12:54 +0100, Neil Armstrong wrote:
>> On the Amlogic Gx SoCs (GXBB, GXL & GXM), the VPU power domain is initialized
>> by the vendor U-Boot code, but running mainline U-boot has been possible
>> on these SoCs. But lacking such init made the system lock at kernel boot.
>>
>> A PM Power Domain driver has been pushed at [1] to solve the main issue.
>> The following patches :
>> - updates the DT bindings accordingly
>> - adds support for missing regulators and registers init
>>
>> Neil Armstrong (4):
>> dt-bindings: display: amlogic,meson-vpu: Add optional power domain
>> property
>> dt-bindings: display: amlogic,meson-dw-hdmi: Add optional HDMI 5V
>> regulator
>> drm/meson: dw_hdmi: Add support for an optional external 5V regulator
>> drm/meson: Add missing VPU init
>>
>> .../devicetree/bindings/display/amlogic,meson-dw-hdmi.txt | 4 ++++
>> .../devicetree/bindings/display/amlogic,meson-vpu.txt | 4 ++++
>> drivers/gpu/drm/meson/meson_drv.c | 9 +++++++++
>> drivers/gpu/drm/meson/meson_dw_hdmi.c | 13
>> +++++++++++++
>> drivers/gpu/drm/meson/meson_registers.h | 4 ++++
>> 5 files changed, 34 insertions(+)
>>
>
> No dependencies on the bootloader anymore, this is great ! Thanks
> Series tested on libretech-cc s905x
>
> Tested-by: Jerome Brunet <jbrunet@baylibre.com>
> Reviewed-by: Jerome Brunet <jbrunet@baylibre.com>
>
Applied to drm-misc-next
^ permalink raw reply
* [PATCH v4 12/12] cpufreq: dt: Reintroduce independent_clocks platform data
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
The Platform data was removed earlier by,
'commit eb96924acddc ("cpufreq: dt: Kill platform-data")'
since there were no users at that time.
Now this is required when the each of the cpu clocks
can be scaled independently, which is the case
for krait cores. So reintroduce it.
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
---
drivers/cpufreq/cpufreq-dt.c | 7 ++++++-
drivers/cpufreq/cpufreq-dt.h | 6 ++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 545946a..8f0e881 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -228,7 +228,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
}
if (fallback) {
- cpumask_setall(policy->cpus);
+ struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data();
+
+ if (!pd || !pd->independent_clocks)
+ cpumask_setall(policy->cpus);
/*
* OPP tables are initialized only for policy->cpu, do it for
@@ -380,6 +383,8 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
if (data && data->have_governor_per_policy)
dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
+ dt_cpufreq_driver.driver_data = data;
+
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)
dev_err(&pdev->dev, "failed register driver: %d\n", ret);
diff --git a/drivers/cpufreq/cpufreq-dt.h b/drivers/cpufreq/cpufreq-dt.h
index 54d774e..dcc03c6 100644
--- a/drivers/cpufreq/cpufreq-dt.h
+++ b/drivers/cpufreq/cpufreq-dt.h
@@ -13,6 +13,12 @@
#include <linux/types.h>
struct cpufreq_dt_platform_data {
+ /*
+ * True when each CPU has its own clock to control its
+ * frequency, false when all CPUs are controlled by a single
+ * clock.
+ */
+ bool independent_clocks;
bool have_governor_per_policy;
};
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 11/12] cpufreq: Add module to register cpufreq on Krait CPUs
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
Register a cpufreq-generic device whenever we detect that a
"qcom,krait" compatible CPU is present in DT.
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++
drivers/cpufreq/Kconfig.arm | 9 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++
4 files changed, 252 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
create mode 100644 drivers/cpufreq/qcom-cpufreq.c
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
new file mode 100644
index 0000000..e7cb104
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
@@ -0,0 +1,38 @@
+Qualcomm Process Voltage Scaling Tables
+
+The node name is required to be "qcom,pvs". There shall only be one
+such node present in the root of the tree.
+
+PROPERTIES
+
+- qcom,pvs-format-a or qcom,pvs-format-b:
+ Usage: required
+ Value type: <empty>
+ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
+ If qcom,pvs-format-a is used the table is two columns
+ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage,
+ and current in that order).
+
+- qcom,speedX-pvsY-bin-vZ:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
+ and version Z.
+Example:
+
+ qcom,pvs {
+ qcom,pvs-format-a;
+ qcom,speed0-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 975000 >,
+ < 594000000 1000000 >,
+ < 702000000 1025000 >,
+ < 810000000 1075000 >,
+ < 918000000 1100000 >,
+ < 1026000000 1125000 >,
+ < 1134000000 1175000 >,
+ < 1242000000 1200000 >,
+ < 1350000000 1225000 >,
+ < 1458000000 1237500 >,
+ < 1512000000 1250000 >;
+ };
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index bdce448..60f28e7 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -100,6 +100,15 @@ config ARM_OMAP2PLUS_CPUFREQ
depends on ARCH_OMAP2PLUS
default ARCH_OMAP2PLUS
+config ARM_QCOM_CPUFREQ
+ tristate "Qualcomm based"
+ depends on ARCH_QCOM
+ select PM_OPP
+ help
+ This adds the CPUFreq driver for Qualcomm SoC based boards.
+
+ If in doubt, say N.
+
config ARM_S3C_CPUFREQ
bool
help
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 812f9e0..1496464 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
new file mode 100644
index 0000000..95ed2ba
--- /dev/null
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -0,0 +1,204 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include "cpufreq-dt.h"
+
+static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
+{
+ void __iomem *base;
+ u32 pte_efuse;
+
+ *speed = *pvs = *pvs_ver = 0;
+
+ base = ioremap(0x007000c0, 4);
+ if (!base) {
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
+ return;
+ }
+
+ pte_efuse = readl_relaxed(base);
+ iounmap(base);
+
+ *speed = pte_efuse & 0xf;
+ if (*speed == 0xf)
+ *speed = (pte_efuse >> 4) & 0xf;
+
+ if (*speed == 0xf) {
+ *speed = 0;
+ pr_warn("Speed bin: Defaulting to %d\n", *speed);
+ } else {
+ pr_info("Speed bin: %d\n", *speed);
+ }
+
+ *pvs = (pte_efuse >> 10) & 0x7;
+ if (*pvs == 0x7)
+ *pvs = (pte_efuse >> 13) & 0x7;
+
+ if (*pvs == 0x7) {
+ *pvs = 0;
+ pr_warn("PVS bin: Defaulting to %d\n", *pvs);
+ } else {
+ pr_info("PVS bin: %d\n", *pvs);
+ }
+}
+
+static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
+{
+ u32 pte_efuse, redundant_sel;
+ void __iomem *base;
+
+ *speed = 0;
+ *pvs = 0;
+ *pvs_ver = 0;
+
+ base = ioremap(0xfc4b80b0, 8);
+ if (!base) {
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
+ return;
+ }
+
+ pte_efuse = readl_relaxed(base);
+ redundant_sel = (pte_efuse >> 24) & 0x7;
+ *speed = pte_efuse & 0x7;
+ /* 4 bits of PVS are in efuse register bits 31, 8-6. */
+ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
+ *pvs_ver = (pte_efuse >> 4) & 0x3;
+
+ switch (redundant_sel) {
+ case 1:
+ *speed = (pte_efuse >> 27) & 0xf;
+ break;
+ case 2:
+ *pvs = (pte_efuse >> 27) & 0xf;
+ break;
+ }
+
+ /* Check SPEED_BIN_BLOW_STATUS */
+ if (pte_efuse & BIT(3)) {
+ pr_info("Speed bin: %d\n", *speed);
+ } else {
+ pr_warn("Speed bin not set. Defaulting to 0!\n");
+ *speed = 0;
+ }
+
+ /* Check PVS_BLOW_STATUS */
+ pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
+ if (pte_efuse) {
+ pr_info("PVS bin: %d\n", *pvs);
+ } else {
+ pr_warn("PVS bin not set. Defaulting to 0!\n");
+ *pvs = 0;
+ }
+
+ pr_info("PVS version: %d\n", *pvs_ver);
+ iounmap(base);
+}
+
+static int __init qcom_cpufreq_populate_opps(void)
+{
+ int len, rows, cols, i, k, speed, pvs, pvs_ver;
+ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
+ struct device_node *np;
+ struct device *dev;
+ int cpu = 0;
+
+ np = of_find_node_by_name(NULL, "qcom,pvs");
+ if (!np)
+ return -ENODEV;
+
+ if (of_property_read_bool(np, "qcom,pvs-format-a")) {
+ get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
+ cols = 2;
+ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
+ get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
+ cols = 3;
+ } else {
+ return -ENODEV;
+ }
+
+ snprintf(table_name, sizeof(table_name),
+ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
+
+ if (!of_find_property(np, table_name, &len))
+ return -EINVAL;
+
+ len /= sizeof(u32);
+ if (len % cols || len == 0)
+ return -EINVAL;
+
+ rows = len / cols;
+
+ for (i = 0, k = 0; i < rows; i++) {
+ u32 freq, volt;
+
+ of_property_read_u32_index(np, table_name, k++, &freq);
+ of_property_read_u32_index(np, table_name, k++, &volt);
+ while (k % cols)
+ k++; /* Skip uA entries if present */
+ for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
+ dev = get_cpu_device(cpu);
+ if (!dev)
+ return -ENODEV;
+ if (dev_pm_opp_add(dev, freq, volt))
+ pr_warn("failed to add OPP %u\n", freq);
+ }
+ }
+
+ return 0;
+}
+
+static int __init qcom_cpufreq_driver_init(void)
+{
+ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
+ struct platform_device_info devinfo = {
+ .name = "cpufreq-dt",
+ .data = &pdata,
+ .size_data = sizeof(pdata),
+ };
+ struct device *cpu_dev;
+ struct device_node *np;
+ int ret;
+
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev)
+ return -ENODEV;
+
+ np = of_node_get(cpu_dev->of_node);
+ if (!np)
+ return -ENOENT;
+
+ if (!of_device_is_compatible(np, "qcom,krait")) {
+ of_node_put(np);
+ return -ENODEV;
+ }
+ of_node_put(np);
+
+ ret = qcom_cpufreq_populate_opps();
+ if (ret)
+ return ret;
+
+ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
+}
+module_init(qcom_cpufreq_driver_init);
+
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
+MODULE_LICENSE("GPL v2");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 10/12] clk: qcom: Add safe switch hook for krait mux clocks
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
When the Hfplls are reprogrammed during the rate change,
the primary muxes which are sourced from the same hfpll
for higher frequencies, needs to be switched to the 'safe
secondary mux' as the parent for that small window. This
is done by registering a clk notifier for the muxes and
switching to the safe parent in the PRE_RATE_CHANGE notifier
and back to the original parent in the POST_RATE_CHANGE notifier.
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
---
drivers/clk/qcom/clk-krait.c | 2 ++
drivers/clk/qcom/clk-krait.h | 3 +++
drivers/clk/qcom/krait-cc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
index 6099307..1627a6e 100644
--- a/drivers/clk/qcom/clk-krait.c
+++ b/drivers/clk/qcom/clk-krait.c
@@ -60,6 +60,8 @@ static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
if (__clk_is_enabled(hw->clk))
__krait_mux_set_sel(mux, sel);
+ mux->reparent = true;
+
return 0;
}
diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
index af89782..f9e1279 100644
--- a/drivers/clk/qcom/clk-krait.h
+++ b/drivers/clk/qcom/clk-krait.h
@@ -23,6 +23,9 @@ struct krait_mux_clk {
u32 shift;
u32 en_mask;
bool lpl;
+ u8 safe_sel;
+ u8 old_index;
+ bool reparent;
struct clk_hw hw;
struct notifier_block clk_nb;
diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
index f5ffb1a..ec899ad 100644
--- a/drivers/clk/qcom/krait-cc.c
+++ b/drivers/clk/qcom/krait-cc.c
@@ -35,6 +35,49 @@
0,
};
+/*
+ * Notifier function for switching the muxes to safe parent
+ * while the hfpll is getting reprogrammed.
+ */
+static int krait_notifier_cb(struct notifier_block *nb,
+ unsigned long event,
+ void *data)
+{
+ int ret = 0;
+ struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk,
+ clk_nb);
+ /* Switch to safe parent */
+ if (event == PRE_RATE_CHANGE) {
+ mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw);
+ ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel);
+ mux->reparent = false;
+ /*
+ * By the time POST_RATE_CHANGE notifier is called,
+ * clk framework itself would have changed the parent for the new rate.
+ * Only otherwise, put back to the old parent.
+ */
+ } else if (event == POST_RATE_CHANGE) {
+ if (!mux->reparent)
+ ret = krait_mux_clk_ops.set_parent(&mux->hw,
+ mux->old_index);
+ }
+
+ return notifier_from_errno(ret);
+}
+
+static int krait_notifier_register(struct device *dev, struct clk *clk,
+ struct krait_mux_clk *mux)
+{
+ int ret = 0;
+
+ mux->clk_nb.notifier_call = krait_notifier_cb;
+ ret = clk_notifier_register(clk, &mux->clk_nb);
+ if (ret)
+ dev_err(dev, "failed to register clock notifier: %d\n", ret);
+
+ return ret;
+}
+
static int
krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
{
@@ -79,6 +122,7 @@
krait_add_sec_mux(struct device *dev, int id, const char *s,
unsigned int offset, bool unique_aux)
{
+ int ret;
struct krait_mux_clk *mux;
static const char *sec_mux_list[] = {
"acpu_aux",
@@ -102,6 +146,7 @@
mux->shift = 2;
mux->parent_map = sec_mux_map;
mux->hw.init = &init;
+ mux->safe_sel = 0;
init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
if (!init.name)
@@ -117,6 +162,11 @@
clk = devm_clk_register(dev, &mux->hw);
+ ret = krait_notifier_register(dev, clk, mux);
+ if (ret)
+ goto unique_aux;
+
+unique_aux:
if (unique_aux)
kfree(sec_mux_list[0]);
err_aux:
@@ -128,6 +178,7 @@
krait_add_pri_mux(struct device *dev, int id, const char *s,
unsigned int offset)
{
+ int ret;
struct krait_mux_clk *mux;
const char *p_names[3];
struct clk_init_data init = {
@@ -148,6 +199,7 @@
mux->lpl = id >= 0;
mux->parent_map = pri_mux_map;
mux->hw.init = &init;
+ mux->safe_sel = 2;
init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
if (!init.name)
@@ -173,6 +225,10 @@
clk = devm_clk_register(dev, &mux->hw);
+ ret = krait_notifier_register(dev, clk, mux);
+ if (ret)
+ goto err_p3;
+err_p3:
kfree(p_names[2]);
err_p2:
kfree(p_names[1]);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 09/12] clk: qcom: Add Krait clock controller driver
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
The Krait CPU clocks are made up of a primary mux and secondary
mux for each CPU and the L2, controlled via cp15 accessors. For
Kraits within KPSSv1 each secondary mux accepts a different aux
source, but on KPSSv2 each secondary mux accepts the same aux
source.
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
drivers/clk/qcom/Kconfig | 8 +
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/krait-cc.c | 350 +++++++++++++++++++++
4 files changed, 381 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
create mode 100644 drivers/clk/qcom/krait-cc.c
diff --git a/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
new file mode 100644
index 0000000..874138f
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
@@ -0,0 +1,22 @@
+Krait Clock Controller
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,krait-cc-v1"
+ "qcom,krait-cc-v2"
+
+- #clock-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 1
+
+Example:
+
+ kraitcc: clock-controller {
+ compatible = "qcom,krait-cc-v1";
+ #clock-cells = <1>;
+ };
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 17dcb88..de6b60d 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -222,6 +222,14 @@ config KPSS_XCC
if you want to support CPU frequency scaling on devices such
as MSM8960, APQ8064, etc.
+config KRAITCC
+ tristate "Krait Clock Controller"
+ depends on COMMON_CLK_QCOM && ARM
+ select KRAIT_CLOCKS
+ help
+ Support for the Krait CPU clocks on Qualcomm devices.
+ Say Y if you want to support CPU frequency scaling.
+
config KRAIT_CLOCKS
bool
select KRAIT_L2_ACCESSORS
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 7ad2302..6e6c700 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
+obj-$(CONFIG_KRAITCC) += krait-cc.o
diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
new file mode 100644
index 0000000..f5ffb1a
--- /dev/null
+++ b/drivers/clk/qcom/krait-cc.c
@@ -0,0 +1,350 @@
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+
+#include "clk-krait.h"
+
+static unsigned int sec_mux_map[] = {
+ 2,
+ 0,
+};
+
+static unsigned int pri_mux_map[] = {
+ 1,
+ 2,
+ 0,
+};
+
+static int
+krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
+{
+ struct krait_div2_clk *div;
+ struct clk_init_data init = {
+ .num_parents = 1,
+ .ops = &krait_div2_clk_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ };
+ const char *p_names[1];
+ struct clk *clk;
+
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return -ENOMEM;
+
+ div->width = 2;
+ div->shift = 6;
+ div->lpl = id >= 0;
+ div->offset = offset;
+ div->hw.init = &init;
+
+ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
+ if (!init.name)
+ return -ENOMEM;
+
+ init.parent_names = p_names;
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
+ if (!p_names[0]) {
+ kfree(init.name);
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_register(dev, &div->hw);
+ kfree(p_names[0]);
+ kfree(init.name);
+
+ return PTR_ERR_OR_ZERO(clk);
+}
+
+static int
+krait_add_sec_mux(struct device *dev, int id, const char *s,
+ unsigned int offset, bool unique_aux)
+{
+ struct krait_mux_clk *mux;
+ static const char *sec_mux_list[] = {
+ "acpu_aux",
+ "qsb",
+ };
+ struct clk_init_data init = {
+ .parent_names = sec_mux_list,
+ .num_parents = ARRAY_SIZE(sec_mux_list),
+ .ops = &krait_mux_clk_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ };
+ struct clk *clk;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ mux->offset = offset;
+ mux->lpl = id >= 0;
+ mux->mask = 0x3;
+ mux->shift = 2;
+ mux->parent_map = sec_mux_map;
+ mux->hw.init = &init;
+
+ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
+ if (!init.name)
+ return -ENOMEM;
+
+ if (unique_aux) {
+ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
+ if (!sec_mux_list[0]) {
+ clk = ERR_PTR(-ENOMEM);
+ goto err_aux;
+ }
+ }
+
+ clk = devm_clk_register(dev, &mux->hw);
+
+ if (unique_aux)
+ kfree(sec_mux_list[0]);
+err_aux:
+ kfree(init.name);
+ return PTR_ERR_OR_ZERO(clk);
+}
+
+static struct clk *
+krait_add_pri_mux(struct device *dev, int id, const char *s,
+ unsigned int offset)
+{
+ struct krait_mux_clk *mux;
+ const char *p_names[3];
+ struct clk_init_data init = {
+ .parent_names = p_names,
+ .num_parents = ARRAY_SIZE(p_names),
+ .ops = &krait_mux_clk_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ };
+ struct clk *clk;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->mask = 0x3;
+ mux->shift = 0;
+ mux->offset = offset;
+ mux->lpl = id >= 0;
+ mux->parent_map = pri_mux_map;
+ mux->hw.init = &init;
+
+ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
+ if (!init.name)
+ return ERR_PTR(-ENOMEM);
+
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
+ if (!p_names[0]) {
+ clk = ERR_PTR(-ENOMEM);
+ goto err_p0;
+ }
+
+ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
+ if (!p_names[1]) {
+ clk = ERR_PTR(-ENOMEM);
+ goto err_p1;
+ }
+
+ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
+ if (!p_names[2]) {
+ clk = ERR_PTR(-ENOMEM);
+ goto err_p2;
+ }
+
+ clk = devm_clk_register(dev, &mux->hw);
+
+ kfree(p_names[2]);
+err_p2:
+ kfree(p_names[1]);
+err_p1:
+ kfree(p_names[0]);
+err_p0:
+ kfree(init.name);
+ return clk;
+}
+
+/* id < 0 for L2, otherwise id == physical CPU number */
+static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
+{
+ int ret;
+ unsigned int offset;
+ void *p = NULL;
+ const char *s;
+ struct clk *clk;
+
+ if (id >= 0) {
+ offset = 0x4501 + (0x1000 * id);
+ s = p = kasprintf(GFP_KERNEL, "%d", id);
+ if (!s)
+ return ERR_PTR(-ENOMEM);
+ } else {
+ offset = 0x500;
+ s = "_l2";
+ }
+
+ ret = krait_add_div(dev, id, s, offset);
+ if (ret) {
+ clk = ERR_PTR(ret);
+ goto err;
+ }
+
+ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
+ if (ret) {
+ clk = ERR_PTR(ret);
+ goto err;
+ }
+
+ clk = krait_add_pri_mux(dev, id, s, offset);
+err:
+ kfree(p);
+ return clk;
+}
+
+static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
+{
+ unsigned int idx = clkspec->args[0];
+ struct clk **clks = data;
+
+ if (idx >= 5) {
+ pr_err("%s: invalid clock index %d\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return clks[idx] ? : ERR_PTR(-ENODEV);
+}
+
+static const struct of_device_id krait_cc_match_table[] = {
+ { .compatible = "qcom,krait-cc-v1", (void *)1UL },
+ { .compatible = "qcom,krait-cc-v2" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, krait_cc_match_table);
+
+static int krait_cc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *id;
+ unsigned long cur_rate, aux_rate;
+ int cpu;
+ struct clk *clk;
+ struct clk **clks;
+ struct clk *l2_pri_mux_clk;
+
+ id = of_match_device(krait_cc_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
+ clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (!id->data) {
+ clk = clk_register_fixed_factor(dev, "acpu_aux",
+ "gpll0_vote", 0, 1, 2);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ }
+
+ /* Krait configurations have at most 4 CPUs and one L2 */
+ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
+ if (!clks)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ clk = krait_add_clks(dev, cpu, id->data);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ clks[cpu] = clk;
+ }
+
+ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
+ if (IS_ERR(l2_pri_mux_clk))
+ return PTR_ERR(l2_pri_mux_clk);
+ clks[4] = l2_pri_mux_clk;
+
+ /*
+ * We don't want the CPU or L2 clocks to be turned off at late init
+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
+ * refcount of these clocks. Any cpufreq/hotplug manager can assume
+ * that the clocks have already been prepared and enabled by the time
+ * they take over.
+ */
+ for_each_online_cpu(cpu) {
+ clk_prepare_enable(l2_pri_mux_clk);
+ WARN(clk_prepare_enable(clks[cpu]),
+ "Unable to turn on CPU%d clock", cpu);
+ }
+
+ /*
+ * Force reinit of HFPLLs and muxes to overwrite any potential
+ * incorrect configuration of HFPLLs and muxes by the bootloader.
+ * While at it, also make sure the cores are running at known rates
+ * and print the current rate.
+ *
+ * The clocks are set to aux clock rate first to make sure the
+ * secondary mux is not sourcing off of QSB. The rate is then set to
+ * two different rates to force a HFPLL reinit under all
+ * circumstances.
+ */
+ cur_rate = clk_get_rate(l2_pri_mux_clk);
+ aux_rate = 384000000;
+ if (cur_rate == 1) {
+ pr_info("L2 @ QSB rate. Forcing new rate.\n");
+ cur_rate = aux_rate;
+ }
+ clk_set_rate(l2_pri_mux_clk, aux_rate);
+ clk_set_rate(l2_pri_mux_clk, 2);
+ clk_set_rate(l2_pri_mux_clk, cur_rate);
+ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
+ for_each_possible_cpu(cpu) {
+ clk = clks[cpu];
+ cur_rate = clk_get_rate(clk);
+ if (cur_rate == 1) {
+ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
+ cur_rate = aux_rate;
+ }
+
+ clk_set_rate(clk, aux_rate);
+ clk_set_rate(clk, 2);
+ clk_set_rate(clk, cur_rate);
+ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
+ }
+
+ of_clk_add_provider(dev->of_node, krait_of_get, clks);
+
+ return 0;
+}
+
+static struct platform_driver krait_cc_driver = {
+ .probe = krait_cc_probe,
+ .driver = {
+ .name = "krait-cc",
+ .of_match_table = krait_cc_match_table,
+ },
+};
+module_platform_driver(krait_cc_driver);
+
+MODULE_DESCRIPTION("Krait CPU Clock Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:krait-cc");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 08/12] clk: qcom: Add KPSS ACC/GCC driver
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
The ACC and GCC regions present in KPSSv1 contain registers to
control clocks and power to each Krait CPU and L2. For CPUfreq
purposes probe these devices and expose a mux clock that chooses
between PXO and PLL8.
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++
.../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++
drivers/clk/qcom/Kconfig | 8 ++
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/kpss-xcc.c | 96 ++++++++++++++++++++++
5 files changed, 140 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
create mode 100644 drivers/clk/qcom/kpss-xcc.c
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
index 1333db9..382a574 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
@@ -21,10 +21,17 @@ PROPERTIES
the register region. An optional second element specifies
the base address and size of the alias register region.
+- clock-output-names:
+ Usage: optional
+ Value type: <string>
+ Definition: Name of the output clock. Typically acpuX_aux where X is a
+ CPU number starting at 0.
+
Example:
clock-controller at 2088000 {
compatible = "qcom,kpss-acc-v2";
reg = <0x02088000 0x1000>,
<0x02008000 0x1000>;
+ clock-output-names = "acpu0_aux";
};
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
new file mode 100644
index 0000000..d1e12f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
@@ -0,0 +1,28 @@
+Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: should be one of:
+ "qcom,kpss-gcc"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: base address and size of the register region
+
+- clock-output-names:
+ Usage: required
+ Value type: <string>
+ Definition: Name of the output clock. Typically acpu_l2_aux indicating
+ an L2 cache auxiliary clock.
+
+Example:
+
+ l2cc: clock-controller at 2011000 {
+ compatible = "qcom,kpss-gcc";
+ reg = <0x2011000 0x1000>;
+ clock-output-names = "acpu_l2_aux";
+ };
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 6592595..17dcb88 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -214,6 +214,14 @@ config QCOM_HFPLL
Say Y if you want to support CPU frequency scaling on devices
such as MSM8974, APQ8084, etc.
+config KPSS_XCC
+ tristate "KPSS Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the Krait ACC and GCC clock controllers. Say Y
+ if you want to support CPU frequency scaling on devices such
+ as MSM8960, APQ8064, etc.
+
config KRAIT_CLOCKS
bool
select KRAIT_L2_ACCESSORS
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index b6741b0..7ad2302 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
+obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
diff --git a/drivers/clk/qcom/kpss-xcc.c b/drivers/clk/qcom/kpss-xcc.c
new file mode 100644
index 0000000..bed51c6
--- /dev/null
+++ b/drivers/clk/qcom/kpss-xcc.c
@@ -0,0 +1,96 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+static const char *aux_parents[] = {
+ "pll8_vote",
+ "pxo",
+};
+
+static unsigned int aux_parent_map[] = {
+ 3,
+ 0,
+};
+
+static const struct of_device_id kpss_xcc_match_table[] = {
+ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
+ { .compatible = "qcom,kpss-gcc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
+
+static int kpss_xcc_driver_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *id;
+ struct clk *clk;
+ struct resource *res;
+ void __iomem *base;
+ const char *name;
+
+ id = of_match_device(kpss_xcc_match_table, &pdev->dev);
+ if (!id)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (id->data) {
+ if (of_property_read_string_index(pdev->dev.of_node,
+ "clock-output-names",
+ 0, &name))
+ return -ENODEV;
+ base += 0x14;
+ } else {
+ name = "acpu_l2_aux";
+ base += 0x28;
+ }
+
+ clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
+ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
+ 0, aux_parent_map, NULL);
+
+ platform_set_drvdata(pdev, clk);
+
+ return PTR_ERR_OR_ZERO(clk);
+}
+
+static int kpss_xcc_driver_remove(struct platform_device *pdev)
+{
+ clk_unregister_mux(platform_get_drvdata(pdev));
+ return 0;
+}
+
+static struct platform_driver kpss_xcc_driver = {
+ .probe = kpss_xcc_driver_probe,
+ .remove = kpss_xcc_driver_remove,
+ .driver = {
+ .name = "kpss-xcc",
+ .of_match_table = kpss_xcc_match_table,
+ },
+};
+module_platform_driver(kpss_xcc_driver);
+
+MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:kpss-xcc");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 07/12] clk: qcom: Add support for Krait clocks
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
The Krait clocks are made up of a series of muxes and a divider
that choose between a fixed rate clock and dedicated HFPLLs for
each CPU. Instead of using mmio accesses to remux parents, the
Krait implementation exposes the remux control via cp15
registers. Support these clocks.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
drivers/clk/qcom/Kconfig | 4 ++
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-krait.c | 134 +++++++++++++++++++++++++++++++++++++++++++
drivers/clk/qcom/clk-krait.h | 48 ++++++++++++++++
4 files changed, 187 insertions(+)
create mode 100644 drivers/clk/qcom/clk-krait.c
create mode 100644 drivers/clk/qcom/clk-krait.h
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 6c811bd..6592595 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -213,3 +213,7 @@ config QCOM_HFPLL
Support for the high-frequency PLLs present on Qualcomm devices.
Say Y if you want to support CPU frequency scaling on devices
such as MSM8974, APQ8084, etc.
+
+config KRAIT_CLOCKS
+ bool
+ select KRAIT_L2_ACCESSORS
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 4a4bf38..b6741b0 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -10,6 +10,7 @@ clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
+clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
new file mode 100644
index 0000000..6099307
--- /dev/null
+++ b/drivers/clk/qcom/clk-krait.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+#include <asm/krait-l2-accessors.h>
+
+#include "clk-krait.h"
+
+/* Secondary and primary muxes share the same cp15 register */
+static DEFINE_SPINLOCK(krait_clock_reg_lock);
+
+#define LPL_SHIFT 8
+static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
+{
+ unsigned long flags;
+ u32 regval;
+
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
+ regval = krait_get_l2_indirect_reg(mux->offset);
+ regval &= ~(mux->mask << mux->shift);
+ regval |= (sel & mux->mask) << mux->shift;
+ if (mux->lpl) {
+ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
+ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
+ }
+ krait_set_l2_indirect_reg(mux->offset, regval);
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
+
+ /* Wait for switch to complete. */
+ mb();
+ udelay(1);
+}
+
+static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
+ u32 sel;
+
+ sel = clk_mux_reindex(index, mux->parent_map, 0);
+ mux->en_mask = sel;
+ /* Don't touch mux if CPU is off as it won't work */
+ if (__clk_is_enabled(hw->clk))
+ __krait_mux_set_sel(mux, sel);
+
+ return 0;
+}
+
+static u8 krait_mux_get_parent(struct clk_hw *hw)
+{
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
+ u32 sel;
+
+ sel = krait_get_l2_indirect_reg(mux->offset);
+ sel >>= mux->shift;
+ sel &= mux->mask;
+ mux->en_mask = sel;
+
+ return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
+}
+
+const struct clk_ops krait_mux_clk_ops = {
+ .set_parent = krait_mux_set_parent,
+ .get_parent = krait_mux_get_parent,
+ .determine_rate = __clk_mux_determine_rate_closest,
+};
+EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
+
+/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
+static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
+ return DIV_ROUND_UP(*parent_rate, 2);
+}
+
+static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
+ unsigned long flags;
+ u32 val;
+ u32 mask = BIT(d->width) - 1;
+
+ if (d->lpl)
+ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
+
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
+ val = krait_get_l2_indirect_reg(d->offset);
+ val &= ~mask;
+ krait_set_l2_indirect_reg(d->offset, val);
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
+
+ return 0;
+}
+
+static unsigned long
+krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
+ u32 mask = BIT(d->width) - 1;
+ u32 div;
+
+ div = krait_get_l2_indirect_reg(d->offset);
+ div >>= d->shift;
+ div &= mask;
+ div = (div + 1) * 2;
+
+ return DIV_ROUND_UP(parent_rate, div);
+}
+
+const struct clk_ops krait_div2_clk_ops = {
+ .round_rate = krait_div2_round_rate,
+ .set_rate = krait_div2_set_rate,
+ .recalc_rate = krait_div2_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
new file mode 100644
index 0000000..af89782
--- /dev/null
+++ b/drivers/clk/qcom/clk-krait.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QCOM_CLK_KRAIT_H
+#define __QCOM_CLK_KRAIT_H
+
+#include <linux/clk-provider.h>
+
+struct krait_mux_clk {
+ unsigned int *parent_map;
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ bool lpl;
+
+ struct clk_hw hw;
+ struct notifier_block clk_nb;
+};
+
+#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
+
+extern const struct clk_ops krait_mux_clk_ops;
+
+struct krait_div2_clk {
+ u32 offset;
+ u8 width;
+ u32 shift;
+ bool lpl;
+
+ struct clk_hw hw;
+};
+
+#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
+
+extern const struct clk_ops krait_div2_clk_ops;
+
+#endif
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 06/12] clk: qcom: Add IPQ806X's HFPLLs
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
Describe the HFPLLs present on IPQ806X devices.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
drivers/clk/qcom/gcc-ipq806x.c | 82 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c
index 28eb200..d571cf8 100644
--- a/drivers/clk/qcom/gcc-ipq806x.c
+++ b/drivers/clk/qcom/gcc-ipq806x.c
@@ -30,6 +30,7 @@
#include "clk-pll.h"
#include "clk-rcg.h"
#include "clk-branch.h"
+#include "clk-hfpll.h"
#include "reset.h"
static struct clk_pll pll0 = {
@@ -113,6 +114,84 @@
},
};
+static struct hfpll_data hfpll0_data = {
+ .mode_reg = 0x3200,
+ .l_reg = 0x3208,
+ .m_reg = 0x320c,
+ .n_reg = 0x3210,
+ .config_reg = 0x3204,
+ .status_reg = 0x321c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3214,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll0 = {
+ .d = &hfpll0_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll0",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
+};
+
+static struct hfpll_data hfpll1_data = {
+ .mode_reg = 0x3240,
+ .l_reg = 0x3248,
+ .m_reg = 0x324c,
+ .n_reg = 0x3250,
+ .config_reg = 0x3244,
+ .status_reg = 0x325c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3314,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll1 = {
+ .d = &hfpll1_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll1",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
+};
+
+static struct hfpll_data hfpll_l2_data = {
+ .mode_reg = 0x3300,
+ .l_reg = 0x3308,
+ .m_reg = 0x330c,
+ .n_reg = 0x3310,
+ .config_reg = 0x3304,
+ .status_reg = 0x331c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3314,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll_l2 = {
+ .d = &hfpll_l2_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll_l2",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
+};
+
static struct clk_pll pll14 = {
.l_reg = 0x31c4,
.m_reg = 0x31c8,
@@ -2800,6 +2879,9 @@ enum {
[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
[NSSTCM_CLK] = &nss_tcm_clk.clkr,
+ [PLL9] = &hfpll0.clkr,
+ [PLL10] = &hfpll1.clkr,
+ [PLL12] = &hfpll_l2.clkr,
};
static const struct qcom_reset_map gcc_ipq806x_resets[] = {
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 05/12] clk: qcom: Add MSM8960/APQ8064's HFPLLs
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
Describe the HFPLLs present on MSM8960 and APQ8064 devices.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
drivers/clk/qcom/gcc-msm8960.c | 172 +++++++++++++++++++++++++++
include/dt-bindings/clock/qcom,gcc-msm8960.h | 2 +
2 files changed, 174 insertions(+)
diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
index eb551c7..809f16a 100644
--- a/drivers/clk/qcom/gcc-msm8960.c
+++ b/drivers/clk/qcom/gcc-msm8960.c
@@ -30,6 +30,7 @@
#include "clk-pll.h"
#include "clk-rcg.h"
#include "clk-branch.h"
+#include "clk-hfpll.h"
#include "reset.h"
static struct clk_pll pll3 = {
@@ -86,6 +87,164 @@
},
};
+static struct hfpll_data hfpll0_data = {
+ .mode_reg = 0x3200,
+ .l_reg = 0x3208,
+ .m_reg = 0x320c,
+ .n_reg = 0x3210,
+ .config_reg = 0x3204,
+ .status_reg = 0x321c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3214,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll0 = {
+ .d = &hfpll0_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll0",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
+};
+
+static struct hfpll_data hfpll1_8064_data = {
+ .mode_reg = 0x3240,
+ .l_reg = 0x3248,
+ .m_reg = 0x324c,
+ .n_reg = 0x3250,
+ .config_reg = 0x3244,
+ .status_reg = 0x325c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3254,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct hfpll_data hfpll1_data = {
+ .mode_reg = 0x3300,
+ .l_reg = 0x3308,
+ .m_reg = 0x330c,
+ .n_reg = 0x3310,
+ .config_reg = 0x3304,
+ .status_reg = 0x331c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3314,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll1 = {
+ .d = &hfpll1_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll1",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
+};
+
+static struct hfpll_data hfpll2_data = {
+ .mode_reg = 0x3280,
+ .l_reg = 0x3288,
+ .m_reg = 0x328c,
+ .n_reg = 0x3290,
+ .config_reg = 0x3284,
+ .status_reg = 0x329c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3294,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll2 = {
+ .d = &hfpll2_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll2",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll2.lock),
+};
+
+static struct hfpll_data hfpll3_data = {
+ .mode_reg = 0x32c0,
+ .l_reg = 0x32c8,
+ .m_reg = 0x32cc,
+ .n_reg = 0x32d0,
+ .config_reg = 0x32c4,
+ .status_reg = 0x32dc,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x32d4,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll3 = {
+ .d = &hfpll3_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll3",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll3.lock),
+};
+
+static struct hfpll_data hfpll_l2_8064_data = {
+ .mode_reg = 0x3300,
+ .l_reg = 0x3308,
+ .m_reg = 0x330c,
+ .n_reg = 0x3310,
+ .config_reg = 0x3304,
+ .status_reg = 0x331c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3314,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct hfpll_data hfpll_l2_data = {
+ .mode_reg = 0x3400,
+ .l_reg = 0x3408,
+ .m_reg = 0x340c,
+ .n_reg = 0x3410,
+ .config_reg = 0x3404,
+ .status_reg = 0x341c,
+ .config_val = 0x7845c665,
+ .droop_reg = 0x3414,
+ .droop_val = 0x0108c000,
+ .min_rate = 600000000UL,
+ .max_rate = 1800000000UL,
+};
+
+static struct clk_hfpll hfpll_l2 = {
+ .d = &hfpll_l2_data,
+ .clkr.hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "pxo" },
+ .num_parents = 1,
+ .name = "hfpll_l2",
+ .ops = &clk_ops_hfpll,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
+};
+
static struct clk_pll pll14 = {
.l_reg = 0x31c4,
.m_reg = 0x31c8,
@@ -3112,6 +3271,9 @@ enum {
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
+ [PLL9] = &hfpll0.clkr,
+ [PLL10] = &hfpll1.clkr,
+ [PLL12] = &hfpll_l2.clkr,
};
static const struct qcom_reset_map gcc_msm8960_resets[] = {
@@ -3323,6 +3485,11 @@ enum {
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
+ [PLL9] = &hfpll0.clkr,
+ [PLL10] = &hfpll1.clkr,
+ [PLL12] = &hfpll_l2.clkr,
+ [PLL16] = &hfpll2.clkr,
+ [PLL17] = &hfpll3.clkr,
};
static const struct qcom_reset_map gcc_apq8064_resets[] = {
@@ -3482,6 +3649,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev)
if (ret)
return ret;
+ if (match->data == &gcc_apq8064_desc) {
+ hfpll1.d = &hfpll1_8064_data;
+ hfpll_l2.d = &hfpll_l2_8064_data;
+ }
+
tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
NULL, 0);
if (IS_ERR(tsens))
diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h
index 7d20eed..e02742f 100644
--- a/include/dt-bindings/clock/qcom,gcc-msm8960.h
+++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h
@@ -319,5 +319,7 @@
#define CE3_SRC 303
#define CE3_CORE_CLK 304
#define CE3_H_CLK 305
+#define PLL16 306
+#define PLL17 307
#endif
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 04/12] clk: qcom: Add HFPLL driver
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
On some devices (MSM8974 for example), the HFPLLs are
instantiated within the Krait processor subsystem as separate
register regions. Add a driver for these PLLs so that we can
provide HFPLL clocks for use by the system.
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++
drivers/clk/qcom/Kconfig | 8 ++
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/hfpll.c | 106 +++++++++++++++++++++
4 files changed, 155 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
create mode 100644 drivers/clk/qcom/hfpll.c
diff --git a/Documentation/devicetree/bindings/clock/qcom,hfpll.txt b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
new file mode 100644
index 0000000..fee92bb
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
@@ -0,0 +1,40 @@
+High-Frequency PLL (HFPLL)
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be "qcom,hfpll"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: address and size of HPLL registers. An optional second
+ element specifies the address and size of the alias
+ register region.
+
+- clock-output-names:
+ Usage: required
+ Value type: <string>
+ Definition: Name of the PLL. Typically hfpllX where X is a CPU number
+ starting at 0. Otherwise hfpll_Y where Y is more specific
+ such as "l2".
+
+Example:
+
+1) An HFPLL for the L2 cache.
+
+ clock-controller at f9016000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf9016000 0x30>;
+ clock-output-names = "hfpll_l2";
+ };
+
+2) An HFPLL for CPU0. This HFPLL has the alias register region.
+
+ clock-controller at f908a000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
+ clock-output-names = "hfpll0";
+ };
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 20b5d6f..6c811bd 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -205,3 +205,11 @@ config SPMI_PMIC_CLKDIV
Technologies, Inc. SPMI PMIC. It configures the frequency of
clkdiv outputs of the PMIC. These clocks are typically wired
through alternate functions on GPIO pins.
+
+config QCOM_HFPLL
+ tristate "High-Frequency PLL (HFPLL) Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the high-frequency PLLs present on Qualcomm devices.
+ Say Y if you want to support CPU frequency scaling on devices
+ such as MSM8974, APQ8084, etc.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 4795e21..4a4bf38 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
diff --git a/drivers/clk/qcom/hfpll.c b/drivers/clk/qcom/hfpll.c
new file mode 100644
index 0000000..7405bb6
--- /dev/null
+++ b/drivers/clk/qcom/hfpll.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+#include "clk-regmap.h"
+#include "clk-hfpll.h"
+
+static const struct hfpll_data hdata = {
+ .mode_reg = 0x00,
+ .l_reg = 0x04,
+ .m_reg = 0x08,
+ .n_reg = 0x0c,
+ .user_reg = 0x10,
+ .config_reg = 0x14,
+ .config_val = 0x430405d,
+ .status_reg = 0x1c,
+ .lock_bit = 16,
+
+ .user_val = 0x8,
+ .user_vco_mask = 0x100000,
+ .low_vco_max_rate = 1248000000,
+ .min_rate = 537600000UL,
+ .max_rate = 2900000000UL,
+};
+
+static const struct of_device_id qcom_hfpll_match_table[] = {
+ { .compatible = "qcom,hfpll" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
+
+static const struct regmap_config hfpll_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x30,
+ .fast_io = true,
+};
+
+static int qcom_hfpll_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ struct regmap *regmap;
+ struct clk_hfpll *h;
+ struct clk_init_data init = {
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_ops_hfpll,
+ };
+
+ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ if (of_property_read_string_index(dev->of_node, "clock-output-names",
+ 0, &init.name))
+ return -ENODEV;
+
+ h->d = &hdata;
+ h->clkr.hw.init = &init;
+ spin_lock_init(&h->lock);
+
+ return devm_clk_register_regmap(&pdev->dev, &h->clkr);
+}
+
+static struct platform_driver qcom_hfpll_driver = {
+ .probe = qcom_hfpll_probe,
+ .driver = {
+ .name = "qcom-hfpll",
+ .of_match_table = qcom_hfpll_match_table,
+ },
+};
+module_platform_driver(qcom_hfpll_driver);
+
+MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-hfpll");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 03/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
HFPLLs are the main frequency source for Krait CPU clocks. Add
support for changing the rate of these PLLs.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
drivers/clk/qcom/clk-hfpll.h | 54 +++++++++
3 files changed, 308 insertions(+)
create mode 100644 drivers/clk/qcom/clk-hfpll.c
create mode 100644 drivers/clk/qcom/clk-hfpll.h
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 602af38..4795e21 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -10,6 +10,7 @@ clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
+clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c
new file mode 100644
index 0000000..5b0bb71
--- /dev/null
+++ b/drivers/clk/qcom/clk-hfpll.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+#include "clk-regmap.h"
+#include "clk-hfpll.h"
+
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+
+/* Initialize a HFPLL at a given rate and enable it. */
+static void __clk_hfpll_init_once(struct clk_hw *hw)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+
+ if (likely(h->init_done))
+ return;
+
+ /* Configure PLL parameters for integer mode. */
+ if (hd->config_val)
+ regmap_write(regmap, hd->config_reg, hd->config_val);
+ regmap_write(regmap, hd->m_reg, 0);
+ regmap_write(regmap, hd->n_reg, 1);
+
+ if (hd->user_reg) {
+ u32 regval = hd->user_val;
+ unsigned long rate;
+
+ rate = clk_hw_get_rate(hw);
+
+ /* Pick the right VCO. */
+ if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
+ regval |= hd->user_vco_mask;
+ regmap_write(regmap, hd->user_reg, regval);
+ }
+
+ if (hd->droop_reg)
+ regmap_write(regmap, hd->droop_reg, hd->droop_val);
+
+ h->init_done = true;
+}
+
+static void __clk_hfpll_enable(struct clk_hw *hw)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+ u32 val;
+
+ __clk_hfpll_init_once(hw);
+
+ /* Disable PLL bypass mode. */
+ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ usleep_range(10, 100);
+
+ /* De-assert active-low PLL reset. */
+ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
+
+ /* Wait for PLL to lock. */
+ if (hd->status_reg) {
+ do {
+ regmap_read(regmap, hd->status_reg, &val);
+ } while (!(val & BIT(hd->lock_bit)));
+ } else {
+ usleep_range(60, 100);
+ }
+
+ /* Enable PLL output. */
+ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
+}
+
+/* Enable an already-configured HFPLL. */
+static int clk_hfpll_enable(struct clk_hw *hw)
+{
+ unsigned long flags;
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+ u32 mode;
+
+ spin_lock_irqsave(&h->lock, flags);
+ regmap_read(regmap, hd->mode_reg, &mode);
+ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
+ __clk_hfpll_enable(hw);
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ return 0;
+}
+
+static void __clk_hfpll_disable(struct clk_hfpll *h)
+{
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+
+ /*
+ * Disable the PLL output, disable test mode, enable the bypass mode,
+ * and assert the reset.
+ */
+ regmap_update_bits(regmap, hd->mode_reg,
+ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
+}
+
+static void clk_hfpll_disable(struct clk_hw *hw)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&h->lock, flags);
+ __clk_hfpll_disable(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ unsigned long rrate;
+
+ rate = clamp(rate, hd->min_rate, hd->max_rate);
+
+ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
+ if (rrate > hd->max_rate)
+ rrate -= *parent_rate;
+
+ return rrate;
+}
+
+/*
+ * For optimization reasons, assumes no downstream clocks are actively using
+ * it.
+ */
+static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+ unsigned long flags;
+ u32 l_val, val;
+ bool enabled;
+
+ l_val = rate / parent_rate;
+
+ spin_lock_irqsave(&h->lock, flags);
+
+ enabled = __clk_is_enabled(hw->clk);
+ if (enabled)
+ __clk_hfpll_disable(h);
+
+ /* Pick the right VCO. */
+ if (hd->user_reg && hd->user_vco_mask) {
+ regmap_read(regmap, hd->user_reg, &val);
+ if (rate <= hd->low_vco_max_rate)
+ val &= ~hd->user_vco_mask;
+ else
+ val |= hd->user_vco_mask;
+ regmap_write(regmap, hd->user_reg, val);
+ }
+
+ regmap_write(regmap, hd->l_reg, l_val);
+
+ if (enabled)
+ __clk_hfpll_enable(hw);
+
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ return 0;
+}
+
+static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+ u32 l_val;
+
+ regmap_read(regmap, hd->l_reg, &l_val);
+
+ return l_val * parent_rate;
+}
+
+static void clk_hfpll_init(struct clk_hw *hw)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+ u32 mode, status;
+
+ regmap_read(regmap, hd->mode_reg, &mode);
+ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
+ __clk_hfpll_init_once(hw);
+ return;
+ }
+
+ if (hd->status_reg) {
+ regmap_read(regmap, hd->status_reg, &status);
+ if (!(status & BIT(hd->lock_bit))) {
+ WARN(1, "HFPLL %s is ON, but not locked!\n",
+ __clk_get_name(hw->clk));
+ clk_hfpll_disable(hw);
+ __clk_hfpll_init_once(hw);
+ }
+ }
+}
+
+static int hfpll_is_enabled(struct clk_hw *hw)
+{
+ struct clk_hfpll *h = to_clk_hfpll(hw);
+ struct hfpll_data const *hd = h->d;
+ struct regmap *regmap = h->clkr.regmap;
+ u32 mode;
+
+ regmap_read(regmap, hd->mode_reg, &mode);
+ mode &= 0x7;
+ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
+}
+
+const struct clk_ops clk_ops_hfpll = {
+ .enable = clk_hfpll_enable,
+ .disable = clk_hfpll_disable,
+ .is_enabled = hfpll_is_enabled,
+ .round_rate = clk_hfpll_round_rate,
+ .set_rate = clk_hfpll_set_rate,
+ .recalc_rate = clk_hfpll_recalc_rate,
+ .init = clk_hfpll_init,
+};
+EXPORT_SYMBOL_GPL(clk_ops_hfpll);
diff --git a/drivers/clk/qcom/clk-hfpll.h b/drivers/clk/qcom/clk-hfpll.h
new file mode 100644
index 0000000..48c18d6
--- /dev/null
+++ b/drivers/clk/qcom/clk-hfpll.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __QCOM_CLK_HFPLL_H__
+#define __QCOM_CLK_HFPLL_H__
+
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include "clk-regmap.h"
+
+struct hfpll_data {
+ u32 mode_reg;
+ u32 l_reg;
+ u32 m_reg;
+ u32 n_reg;
+ u32 user_reg;
+ u32 droop_reg;
+ u32 config_reg;
+ u32 status_reg;
+ u8 lock_bit;
+
+ u32 droop_val;
+ u32 config_val;
+ u32 user_val;
+ u32 user_vco_mask;
+ unsigned long low_vco_max_rate;
+
+ unsigned long min_rate;
+ unsigned long max_rate;
+};
+
+struct clk_hfpll {
+ struct hfpll_data const *d;
+ int init_done;
+
+ struct clk_regmap clkr;
+ spinlock_t lock;
+};
+
+#define to_clk_hfpll(_hw) \
+ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
+
+extern const struct clk_ops clk_ops_hfpll;
+
+#endif
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 02/12] clk: mux: Split out register accessors for reuse
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
We want to reuse the logic in clk-mux.c for other clock drivers
that don't use readl as register accessors. Fortunately, there
really isn't much to the mux code besides the table indirection
and quirk flags if you assume any bit shifting and masking has
been done already. Pull that logic out into reusable functions
that operate on an optional table and some flags so that other
drivers can use the same logic.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
drivers/clk/clk-mux.c | 75 +++++++++++++++++++++++++++-----------------
include/linux/clk-provider.h | 9 ++++--
2 files changed, 54 insertions(+), 30 deletions(-)
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 39cabe1..22ebf99 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -26,35 +26,24 @@
* parent - parent is adjustable through clk_set_parent
*/
-static u8 clk_mux_get_parent(struct clk_hw *hw)
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
+ unsigned int *table, unsigned long flags)
{
- struct clk_mux *mux = to_clk_mux(hw);
int num_parents = clk_hw_get_num_parents(hw);
- u32 val;
-
- /*
- * FIXME need a mux-specific flag to determine if val is bitwise or numeric
- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
- * to 0x7 (index starts at one)
- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
- * val = 0x4 really means "bit 2, index starts at bit 0"
- */
- val = clk_readl(mux->reg) >> mux->shift;
- val &= mux->mask;
- if (mux->table) {
+ if (table) {
int i;
for (i = 0; i < num_parents; i++)
- if (mux->table[i] == val)
+ if (table[i] == val)
return i;
return -EINVAL;
}
- if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+ if (val && (flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1;
- if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+ if (val && (flags & CLK_MUX_INDEX_ONE))
val--;
if (val >= num_parents)
@@ -62,23 +51,53 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
return val;
}
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+static u8 _clk_mux_get_parent(struct clk_hw *hw)
{
struct clk_mux *mux = to_clk_mux(hw);
u32 val;
- unsigned long flags = 0;
- if (mux->table) {
- index = mux->table[index];
+ /*
+ * FIXME need a mux-specific flag to determine if val is bitwise or
+ * numeric e.g. sys_clkin_ck's clksel field is 3 bits wide,
+ * but ranges from 0x1 to 0x7 (index starts at one)
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+ * val = 0x4 really means "bit 2, index starts at bit 0"
+ */
+ val = clk_readl(mux->reg) >> mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_get_parent(hw, val, mux->table, mux->flags);
+}
+
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
+ unsigned long flags)
+{
+ unsigned int val = index;
+
+ if (table) {
+ val = table[val];
} else {
- if (mux->flags & CLK_MUX_INDEX_BIT)
- index = 1 << index;
+ if (flags & CLK_MUX_INDEX_BIT)
+ val = 1 << index;
- if (mux->flags & CLK_MUX_INDEX_ONE)
- index++;
+ if (flags & CLK_MUX_INDEX_ONE)
+ val++;
}
+ return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_reindex);
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+ unsigned long flags = 0;
+
+ index = clk_mux_reindex(index, mux->table, mux->flags);
+
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);
else
@@ -102,14 +121,14 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
}
const struct clk_ops clk_mux_ops = {
- .get_parent = clk_mux_get_parent,
+ .get_parent = _clk_mux_get_parent,
.set_parent = clk_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_mux_ops);
const struct clk_ops clk_mux_ro_ops = {
- .get_parent = clk_mux_get_parent,
+ .get_parent = _clk_mux_get_parent,
};
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
@@ -117,7 +136,7 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
{
struct clk_mux *mux;
struct clk_hw *hw;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 7c925e6..9578411 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -468,7 +468,7 @@ struct clk_hw *clk_hw_register_divider_table(struct device *dev,
struct clk_mux {
struct clk_hw hw;
void __iomem *reg;
- u32 *table;
+ unsigned int *table;
u32 mask;
u8 shift;
u8 flags;
@@ -486,6 +486,11 @@ struct clk_mux {
extern const struct clk_ops clk_mux_ops;
extern const struct clk_ops clk_mux_ro_ops;
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
+ unsigned int *table, unsigned long flags);
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
+ unsigned long flags);
+
struct clk *clk_register_mux(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
@@ -506,7 +511,7 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table, spinlock_t *lock);
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
void clk_unregister_mux(struct clk *clk);
void clk_hw_unregister_mux(struct clk_hw *hw);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 01/12] ARM: Add Krait L2 register accessor functions
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512726150-7204-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
Krait CPUs have a handful of L2 cache controller registers that
live behind a cp15 based indirection register. First you program
the indirection register (l2cpselr) to point the L2 'window'
register (l2cpdr) at what you want to read/write. Then you
read/write the 'window' register to do what you want. The
l2cpselr register is not banked per-cpu so we must lock around
accesses to it to prevent other CPUs from re-pointing l2cpdr
underneath us.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Courtney Cavin <courtney.cavin@sonymobile.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
arch/arm/common/Kconfig | 3 ++
arch/arm/common/Makefile | 1 +
arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++
arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
4 files changed, 82 insertions(+)
create mode 100644 arch/arm/common/krait-l2-accessors.c
create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index e5ad070..c8e1986 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -7,6 +7,9 @@ config DMABOUNCE
bool
select ZONE_DMA
+config KRAIT_L2_ACCESSORS
+ bool
+
config SHARP_LOCOMO
bool
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 70b4a14..eec6cd1 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -7,6 +7,7 @@ obj-y += firmware.o
obj-$(CONFIG_SA1111) += sa1111.o
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
obj-$(CONFIG_SHARP_SCOOP) += scoop.o
diff --git a/arch/arm/common/krait-l2-accessors.c b/arch/arm/common/krait-l2-accessors.c
new file mode 100644
index 0000000..5d514bb
--- /dev/null
+++ b/arch/arm/common/krait-l2-accessors.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/export.h>
+
+#include <asm/barrier.h>
+#include <asm/krait-l2-accessors.h>
+
+static DEFINE_RAW_SPINLOCK(krait_l2_lock);
+
+void krait_set_l2_indirect_reg(u32 addr, u32 val)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
+ /*
+ * Select the L2 window by poking l2cpselr, then write to the window
+ * via l2cpdr.
+ */
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
+ isb();
+ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
+ isb();
+
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
+}
+EXPORT_SYMBOL(krait_set_l2_indirect_reg);
+
+u32 krait_get_l2_indirect_reg(u32 addr)
+{
+ u32 val;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
+ /*
+ * Select the L2 window by poking l2cpselr, then read from the window
+ * via l2cpdr.
+ */
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
+ isb();
+ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
+
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
+
+ return val;
+}
+EXPORT_SYMBOL(krait_get_l2_indirect_reg);
diff --git a/arch/arm/include/asm/krait-l2-accessors.h b/arch/arm/include/asm/krait-l2-accessors.h
new file mode 100644
index 0000000..48fe552
--- /dev/null
+++ b/arch/arm/include/asm/krait-l2-accessors.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
+
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
+extern u32 krait_get_l2_indirect_reg(u32 addr);
+
+#endif
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [RESEND PATCH v4 00/12] Krait clocks + Krait CPUfreq
From: Sricharan R @ 2017-12-08 9:42 UTC (permalink / raw)
To: linux-arm-kernel
Mostly a resend of the v3 posted by Stephen quite some time back [1]
except for few changes.
Based on reading some feedback from list,
* Dropped the patch "clk: Add safe switch hook" from v3 [2].
Now this is taken care by patch#10 in this series only for Krait.
* Dropped the path "clk: Avoid sending high rates to downstream
clocks during set_rate" from v3 [3].
* Rebased on top of clk-next.
* Dropped the DT update from the series. Will send seperately
* Now with cpufreq-dt+opp supporting voltage scaling, registering the
krait cpu supplies in DT should be sufficient. But one issue is,
the qcom-cpufreq drivers reads the efuse and based on that registers
the opp data and then registers the cpufreq-dt device. So when
cpufreq-dt driver probes and registers the regulator to the OPP framework,
it expects that the opp data for the device should not be registered before
the regulator. Will send a RFC patch removing that check, to find out the
right way of doing it.
These patches provide cpufreq scaling on devices with Krait CPUs.
In Krait CPU designs there's one PLL and two muxes per CPU, allowing
us to switch CPU frequencies independently.
secondary
+-----+ +
| QSB |-------+------------|\
+-----+ | | |-+
| +-------|/ |
| | + |
+-----+ | | |
| PLL |----+-------+ | primary
+-----+ | | | +
| | +-----|\ +------+
+-------+ | | | \ | |
| HFPLL |----------+-----------------| |-----| CPU0 |
+-------+ | | | | | | |
| | | +-----+ | / +------+
| | +-| / 2 |---------|/
| | +-----+ +
| | secondary
| | +
| +------------|\
| | |-+
+---------------|/ | primary
+ | +
+-----|\ +------+
+-------+ | \ | |
| HFPLL |----------------------------| |-----| CPU1 |
+-------+ | | | | |
| +-----+ | / +------+
+-| / 2 |---------|/
+-----+ +
To support this in the common clock framework we model the muxes,
dividers, and PLLs as different clocks. CPUfreq only interacts
with the primary mux (farthest right in the diagram). When CPUfreq
sets a rate, the mux code finds the best parent that can provide the rate.
Due to the design, QSB and the top PLL are always a fixed rate and thus
only support one frequency each. These sources provide the lowest
frequencies for the CPUs. The HFPLLs are where we can make the CPU go
faster (GHz range). Sometimes we need to run the HFPLL twice as
fast and divide it by two to get a particular frequency.
When switching rates we can't leave the CPU clocked by the HFPLL because
we need to turn off the output of the PLL when changing its frequency.
This means we have to switch over to the secondary mux and use one of the
fixed sources. This is why we need something like the safe parent patch.
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/332607.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/332615.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/332608.html
Sricharan R (2):
clk: qcom: Add safe switch hook for krait mux clocks
cpufreq: dt: Reintroduce independent_clocks platform data
Stephen Boyd (10):
ARM: Add Krait L2 register accessor functions
clk: mux: Split out register accessors for reuse
clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
clk: qcom: Add HFPLL driver
clk: qcom: Add MSM8960/APQ8064's HFPLLs
clk: qcom: Add IPQ806X's HFPLLs
clk: qcom: Add support for Krait clocks
clk: qcom: Add KPSS ACC/GCC driver
clk: qcom: Add Krait clock controller driver
cpufreq: Add module to register cpufreq on Krait CPUs
.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 +
.../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 ++
.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++
.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++
.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
arch/arm/common/Kconfig | 3 +
arch/arm/common/Makefile | 1 +
arch/arm/common/krait-l2-accessors.c | 58 +++
arch/arm/include/asm/krait-l2-accessors.h | 20 +
drivers/clk/clk-mux.c | 75 ++--
drivers/clk/qcom/Kconfig | 28 ++
drivers/clk/qcom/Makefile | 5 +
drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++
drivers/clk/qcom/clk-hfpll.h | 54 +++
drivers/clk/qcom/clk-krait.c | 136 +++++++
drivers/clk/qcom/clk-krait.h | 51 +++
drivers/clk/qcom/gcc-ipq806x.c | 82 +++++
drivers/clk/qcom/gcc-msm8960.c | 172 +++++++++
drivers/clk/qcom/hfpll.c | 106 ++++++
drivers/clk/qcom/kpss-xcc.c | 96 +++++
drivers/clk/qcom/krait-cc.c | 406 +++++++++++++++++++++
drivers/cpufreq/Kconfig.arm | 9 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/cpufreq-dt.c | 7 +-
drivers/cpufreq/cpufreq-dt.h | 6 +
drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++
include/dt-bindings/clock/qcom,gcc-msm8960.h | 2 +
include/linux/clk-provider.h | 9 +-
28 files changed, 1888 insertions(+), 31 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
create mode 100644 arch/arm/common/krait-l2-accessors.c
create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
create mode 100644 drivers/clk/qcom/clk-hfpll.c
create mode 100644 drivers/clk/qcom/clk-hfpll.h
create mode 100644 drivers/clk/qcom/clk-krait.c
create mode 100644 drivers/clk/qcom/clk-krait.h
create mode 100644 drivers/clk/qcom/hfpll.c
create mode 100644 drivers/clk/qcom/kpss-xcc.c
create mode 100644 drivers/clk/qcom/krait-cc.c
create mode 100644 drivers/cpufreq/qcom-cpufreq.c
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* [PATCH] arm64: dts: allwinner: a64: bananapi-m64: Add LED device node
From: Maxime Ripard @ 2017-12-08 9:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v64HrvHZ-o7dRb2shUBUnwziV9ZVFVEA25+nyBrN+LffpA@mail.gmail.com>
On Tue, Dec 05, 2017 at 09:00:37PM +0800, Chen-Yu Tsai wrote:
> On Tue, Dec 5, 2017 at 6:05 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > Hi,
> >
> > On Sat, Dec 02, 2017 at 12:27:33AM +0800, Chen-Yu Tsai wrote:
> >> The Bananapi-M64 has 3 LEDS in red, green, and blue. These are toggled
> >> via GPIO lines, which drive transistors that control current across the
> >> LEDS. The red LED is by default on, via an additional pull-up on the
> >> control line. We consider this means that it is a power indicator.
> >> So we set the "default-on" property for it.
> >>
> >> The pingroups the GPIO lines belong to require external regulators be
> >> enabled to be able to drive the GPIO high. These regulators also have
> >> other purposes. However the pin controller does not have bindings for
> >> regulators. Here we just set them to always-on.
> >
> > I guess we should take the opportunity to do just that.
> >
> > We have been deferring this for quite some time now, this is a perfect
> > occasion to do it once and for all :)
>
> True. But right now I don't see an elegant solution. What is the purpose
> of having the regulator tied to pinctrl / GPIO? You can't have the pinctrl
> driver asking for the regulator at probe time, as it might not be available
> or even have a cyclic dependency (because the PMIC is I2C driven, which
> needs an I2C controller, which needs the pinctrl).
Ah, right, you definitely have a circular dependency.
> If you defer fetching the regulator to a later point, you might end
> up with an inconsistent state. Again with the I2C driven regulator
> example, what happens if the regulator core decides to turn the
> regulator off before the pinctrl side has a chance to take a
> reference and enable it?
I guess you could implement something based on the an asynchronous
regulator_get. You call regulator_get_async (or whatever) in the
pinctrl probe, and get notified as soon as it is registered so that
you can act upon it.
Anyway, this is a bit too much to ask to enable 3 LEDs... I've applied
that patch.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171208/9c1af039/attachment-0001.sig>
^ permalink raw reply
* [PATCH 4/7 v2] net: ethernet: i825xx: Fix platform_get_irq's error checking
From: arvindY @ 2017-12-08 9:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <d0b7e1cc-4190-437a-6461-35cee6edc203@cogentembedded.com>
Hi David,
On Wednesday 06 December 2017 05:49 PM, Sergei Shtylyov wrote:
> On 12/05/2017 06:49 PM, David Miller wrote:
>
>>>> From: Arvind Yadav <arvind.yadav.cs@gmail.com>
>>>> Date: Mon, 4 Dec 2017 23:18:20 +0530
>>>>
>>>>> @@ -120,9 +120,10 @@ static int sni_82596_probe(struct
>>>>> platform_device
>>>>> *dev)
>>>>> netdevice->dev_addr[5] = readb(eth_addr + 0x06);
>>>>> iounmap(eth_addr);
>>>>> - if (!netdevice->irq) {
>>>>> + if (netdevice->irq <= 0) {
>>>>> printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n",
>>>>> __FILE__, netdevice->base_addr);
>>>>> + retval = netdevice->irq ? netdevice->irq : -ENODEV;
>>>>> goto probe_failed;
>>>>> }
>>>> Ok, thinking about this some more...
>>>>
>>>> It is impossible to use platform_get_irq() without every single call
>>>> site having this funny:
>>>>
>>>> ret = val ? val : -ENODEV;
>>>>
>>>> sequence.
>>>>
>>>> This is unnecessary duplication and it is also error prone, so I
>>>> really think this logic belongs in platform_get_irq() itself. It can
>>>> convert '0' to -ENODEV and that way we need no special logic in the
>>>> callers at all.
>>> platform_get_irq() will return 0 only for sparc, If sparc initialize
>>> platform
>>> data irq[PROMINTR_MAX] as zero. Otherwise platform_get_irq() will
>>> never return
>>> 0. It will return either IRQ number or error (as negative number). But
>>> I am getting
>>> review comment by reviewer/maintainer in other subsystem to add check
>>> for
>>> zero. So I have done same changes here. Please correct me if i am
>>> wrong.
>>
>> If you make the change that I suggest, you instead can check for
>
> I assume such change is needed only for the SPARC-specific section
> of platform_get_irq()?
>
>> '-ENODEV' to mean no IRQ.
>
> No specific error check is needed, just irq < 0 check should be
> enough...
> Also, looking at platform_get_irq(), -ENXIO should be returned in this
> case.
>
> MBR, Sergei
Is it ok. If We will add a check for only < 0.
Regards
Arvind
^ permalink raw reply
* [PATCH v4 00/12] Krait clocks + Krait CPUfreq
From: Sricharan R @ 2017-12-08 9:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512725401-4944-1-git-send-email-sricharan@codeaurora.org>
oops, got Mike's id wrong. Will just re-post fixing his id.
On 12/8/2017 2:59 PM, Sricharan R wrote:
> Mostly a resend of the v3 posted by Stephen quite some time back [1]
> except for few changes.
> Based on reading some feedback from list,
> * Dropped the patch "clk: Add safe switch hook" from v3 [2].
> Now this is taken care by patch#10 in this series only for Krait.
> * Dropped the path "clk: Avoid sending high rates to downstream
> clocks during set_rate" from v3 [3].
> * Rebased on top of clk-next.
> * Dropped the DT update from the series. Will send seperately
> * Now with cpufreq-dt+opp supporting voltage scaling, registering the
> krait cpu supplies in DT should be sufficient. But one issue is,
> the qcom-cpufreq drivers reads the efuse and based on that registers
> the opp data and then registers the cpufreq-dt device. So when
> cpufreq-dt driver probes and registers the regulator to the OPP framework,
> it expects that the opp data for the device should not be registered before
> the regulator. Will send a RFC patch removing that check, to find out the
> right way of doing it.
>
> These patches provide cpufreq scaling on devices with Krait CPUs.
> In Krait CPU designs there's one PLL and two muxes per CPU, allowing
> us to switch CPU frequencies independently.
>
> secondary
> +-----+ +
> | QSB |-------+------------|\
> +-----+ | | |-+
> | +-------|/ |
> | | + |
> +-----+ | | |
> | PLL |----+-------+ | primary
> +-----+ | | | +
> | | +-----|\ +------+
> +-------+ | | | \ | |
> | HFPLL |----------+-----------------| |-----| CPU0 |
> +-------+ | | | | | | |
> | | | +-----+ | / +------+
> | | +-| / 2 |---------|/
> | | +-----+ +
> | | secondary
> | | +
> | +------------|\
> | | |-+
> +---------------|/ | primary
> + | +
> +-----|\ +------+
> +-------+ | \ | |
> | HFPLL |----------------------------| |-----| CPU1 |
> +-------+ | | | | |
> | +-----+ | / +------+
> +-| / 2 |---------|/
> +-----+ +
>
> To support this in the common clock framework we model the muxes,
> dividers, and PLLs as different clocks. CPUfreq only interacts
> with the primary mux (farthest right in the diagram). When CPUfreq
> sets a rate, the mux code finds the best parent that can provide the rate.
> Due to the design, QSB and the top PLL are always a fixed rate and thus
> only support one frequency each. These sources provide the lowest
> frequencies for the CPUs. The HFPLLs are where we can make the CPU go
> faster (GHz range). Sometimes we need to run the HFPLL twice as
> fast and divide it by two to get a particular frequency.
>
> When switching rates we can't leave the CPU clocked by the HFPLL because
> we need to turn off the output of the PLL when changing its frequency.
> This means we have to switch over to the secondary mux and use one of the
> fixed sources. This is why we need something like the safe parent patch.
>
> [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/332607.html
> [2] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/332615.html
> [3] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/332608.html
>
> Sricharan R (2):
> clk: qcom: Add safe switch hook for krait mux clocks
> cpufreq: dt: Reintroduce independent_clocks platform data
>
> Stephen Boyd (10):
> ARM: Add Krait L2 register accessor functions
> clk: mux: Split out register accessors for reuse
> clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
> clk: qcom: Add HFPLL driver
> clk: qcom: Add MSM8960/APQ8064's HFPLLs
> clk: qcom: Add IPQ806X's HFPLLs
> clk: qcom: Add support for Krait clocks
> clk: qcom: Add KPSS ACC/GCC driver
> clk: qcom: Add Krait clock controller driver
> cpufreq: Add module to register cpufreq on Krait CPUs
>
> .../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 +
> .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 ++
> .../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++
> .../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++
> .../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
> arch/arm/common/Kconfig | 3 +
> arch/arm/common/Makefile | 1 +
> arch/arm/common/krait-l2-accessors.c | 58 +++
> arch/arm/include/asm/krait-l2-accessors.h | 20 +
> drivers/clk/clk-mux.c | 75 ++--
> drivers/clk/qcom/Kconfig | 28 ++
> drivers/clk/qcom/Makefile | 5 +
> drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++
> drivers/clk/qcom/clk-hfpll.h | 54 +++
> drivers/clk/qcom/clk-krait.c | 136 +++++++
> drivers/clk/qcom/clk-krait.h | 51 +++
> drivers/clk/qcom/gcc-ipq806x.c | 82 +++++
> drivers/clk/qcom/gcc-msm8960.c | 172 +++++++++
> drivers/clk/qcom/hfpll.c | 106 ++++++
> drivers/clk/qcom/kpss-xcc.c | 96 +++++
> drivers/clk/qcom/krait-cc.c | 406 +++++++++++++++++++++
> drivers/cpufreq/Kconfig.arm | 9 +
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/cpufreq-dt.c | 7 +-
> drivers/cpufreq/cpufreq-dt.h | 6 +
> drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++
> include/dt-bindings/clock/qcom,gcc-msm8960.h | 2 +
> include/linux/clk-provider.h | 9 +-
> 28 files changed, 1888 insertions(+), 31 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
> create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
> create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
> create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
> create mode 100644 arch/arm/common/krait-l2-accessors.c
> create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
> create mode 100644 drivers/clk/qcom/clk-hfpll.c
> create mode 100644 drivers/clk/qcom/clk-hfpll.h
> create mode 100644 drivers/clk/qcom/clk-krait.c
> create mode 100644 drivers/clk/qcom/clk-krait.h
> create mode 100644 drivers/clk/qcom/hfpll.c
> create mode 100644 drivers/clk/qcom/kpss-xcc.c
> create mode 100644 drivers/clk/qcom/krait-cc.c
> create mode 100644 drivers/cpufreq/qcom-cpufreq.c
>
--
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
^ permalink raw reply
* [PATCH v5 2/4] clk: meson-axg: add clocks dt-bindings required header
From: Jerome Brunet @ 2017-12-08 9:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171207221014.6qwcb43w5hms4ma5@rob-hp-laptop>
On Thu, 2017-12-07 at 16:10 -0600, Rob Herring wrote:
> On Thu, Dec 07, 2017 at 05:52:58PM +0800, Yixun Lan wrote:
> > From: Qiufang Dai <qiufang.dai@amlogic.com>
> >
> > Add the required header for the clocks ID dt-bindings
> > exported from various subsystem in the Meson-AXG SoC.
> >
> > Signed-off-by: Qiufang Dai <qiufang.dai@amlogic.com>
> > Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
> > ---
> > include/dt-bindings/clock/axg-clkc.h | 71
> > ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 71 insertions(+)
> > create mode 100644 include/dt-bindings/clock/axg-clkc.h
>
> Please add acks when posting new versions.
>
> Rob
Yixun, please be consistent about this. Maintainers are not going to dig
through your previous revision to collect Tags
I believe Neil acked patch 3, didn't he ?
Please resend your series with the Tags collected
If not already done, please make sure patches 1-3 apply on top of rc1 and patch
4 on Kevin's dt64 branch.
Thanks
Jerome
^ permalink raw reply
* [PATCH v8 4/6] clocksource: stm32: only use 32 bits timers
From: Benjamin Gaignard @ 2017-12-08 9:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <56ba2617-ab7b-9715-928e-c922c1b32866@linaro.org>
2017-12-08 10:29 GMT+01:00 Daniel Lezcano <daniel.lezcano@linaro.org>:
> On 08/12/2017 10:25, Benjamin Gaignard wrote:
>> 2017-12-08 9:34 GMT+01:00 Daniel Lezcano <daniel.lezcano@linaro.org>:
>>> On 14/11/2017 09:52, Benjamin Gaignard wrote:
>>>> The clock driving counters is at 90MHz so the maximum period
>>>> for 16 bis counters is around 750 ms
>>>
>>> 728 us
>>>
>>>> which is a short period for a clocksource.
>>>
>>> Which clocksource are you talking about ?
>>>
>>>> For 32 bits counters this period is close
>>>> 47 secondes which is more acceptable.
>>>>
>>>> This patch remove 16 bits counters support and makes sure that
>>>> they won't be probed anymore.
>>>
>>> Are we talking about clockevent or clocksource?
>>>
>>> Is this issue present today ? Or is it if we add the clocksource support
>>> ? We are talking about clocksource but we change the clockevent code.
>>>
>>> All this is very confusing.
>>>
>>> I have a rough idea of what is happening, but it is not up to me to
>>> decode and infer from the changes, you need to describe *clearly* the
>>> situation.
>>>
>>> - What happens if we use a 16bits timer as a clockevent ?
>>> - What happens if we use a 16bits timer as a clocksource ?
>>> - Why is it preferable to remove the support of the 16bits timers
>>> instead of downgrading them with the rating ?
>>
>> Up to this patch it is only about clockevent, clocksource code is
>> introduced in patch 5.
>> For the both cases 16bits counter have a a too short period (728us)
>> and can't be used
>> so downgrading the rating is not a solution.
>
> You have to explain why it is a too short period. I will be happy to see
> an example of the issues the user is facing.
This a very basic issue, the kernel doesn't boot at all...
>
>
>
>> I will change the wording in v9
>>
>>>
>>>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
>>>
>>>> ---
>>>> drivers/clocksource/timer-stm32.c | 26 ++++++++++++--------------
>>>> 1 file changed, 12 insertions(+), 14 deletions(-)
>>>>
>>>> diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
>>>> index ae41a19..8173bcf 100644
>>>> --- a/drivers/clocksource/timer-stm32.c
>>>> +++ b/drivers/clocksource/timer-stm32.c
>>>> @@ -83,9 +83,9 @@ static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
>>>> static int __init stm32_clockevent_init(struct device_node *node)
>>>> {
>>>> struct reset_control *rstc;
>>>> - unsigned long max_delta;
>>>> - int ret, bits, prescaler = 1;
>>>> + unsigned long max_arr;
>>>> struct timer_of *to;
>>>> + int ret;
>>>>
>>>> to = kzalloc(sizeof(*to), GFP_KERNEL);
>>>> if (!to)
>>>> @@ -115,29 +115,27 @@ static int __init stm32_clockevent_init(struct device_node *node)
>>>>
>>>> /* Detect whether the timer is 16 or 32 bits */
>>>> writel_relaxed(~0U, timer_of_base(to) + TIM_ARR);
>>>> - max_delta = readl_relaxed(timer_of_base(to) + TIM_ARR);
>>>> - if (max_delta == ~0U) {
>>>> - prescaler = 1;
>>>> - bits = 32;
>>>> - } else {
>>>> - prescaler = 1024;
>>>> - bits = 16;
>>>> + max_arr = readl_relaxed(timer_of_base(to) + TIM_ARR);
>>>> + if (max_arr != ~0U) {
>>>> + pr_err("32 bits timer is needed\n");
>>>> + ret = -EINVAL;
>>>> + goto deinit;
>>>> }
>>>
>>> Wrap this in a function:
>>>
>>> static bool stm32_timer_is_32bits(struct timer_of *to)
>>> {
>>> return readl_relaxed(timer_of_base(to) + TIM_ARR) == ~0UL;
>>> }
>>>
>>> Then clearly inform the user.
>>>
>>> if (!stm32_timer_is_32bits(to)) {
>>> pr_warn("Timer %pOF is a 16 bits timer\n", node);
>>> /* abort the registration or downgrade the timer's rating */
>>> }
>>
>> Ok I will change that in v9
>>
>>>
>>>> +
>>>> writel_relaxed(0, timer_of_base(to) + TIM_ARR);
>>>>
>>>> - writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC);
>>>> + writel_relaxed(0, timer_of_base(to) + TIM_PSC);
>>>> writel_relaxed(TIM_EGR_UG, timer_of_base(to) + TIM_EGR);
>>>> writel_relaxed(TIM_DIER_UIE, timer_of_base(to) + TIM_DIER);
>>>> writel_relaxed(0, timer_of_base(to) + TIM_SR);
>>>>
>>>> clockevents_config_and_register(&to->clkevt,
>>>> - timer_of_period(to), MIN_DELTA, max_delta);
>>>> -
>>>> - pr_info("%pOF: STM32 clockevent driver initialized (%d bits)\n",
>>>> - node, bits);
>>>> + timer_of_period(to), MIN_DELTA, ~0U);
>>>>
>>>> return 0;
>>>>
>>>> +deinit:
>>>> + timer_of_exit(to);
>>>
>>> Fix this please (timer_of_cleanup).
>>>
>>> In the future, make sure the patches are git-bisect safe.
>>>
>>>
>>>
>>> --
>>> <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs
>>>
>>> Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
>>> <http://twitter.com/#!/linaroorg> Twitter |
>>> <http://www.linaro.org/linaro-blog/> Blog
>
>
> --
> <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs
>
> Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
> <http://twitter.com/#!/linaroorg> Twitter |
> <http://www.linaro.org/linaro-blog/> Blog
>
--
Benjamin Gaignard
Graphic Study Group
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH v4 12/12] cpufreq: dt: Reintroduce independent_clocks platform data
From: Sricharan R @ 2017-12-08 9:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512725401-4944-1-git-send-email-sricharan@codeaurora.org>
The Platform data was removed earlier by,
'commit eb96924acddc ("cpufreq: dt: Kill platform-data")'
since there were no users at that time.
Now this is required when the each of the cpu clocks
can be scaled independently, which is the case
for krait cores. So reintroduce it.
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
---
drivers/cpufreq/cpufreq-dt.c | 7 ++++++-
drivers/cpufreq/cpufreq-dt.h | 6 ++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 545946a..8f0e881 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -228,7 +228,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
}
if (fallback) {
- cpumask_setall(policy->cpus);
+ struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data();
+
+ if (!pd || !pd->independent_clocks)
+ cpumask_setall(policy->cpus);
/*
* OPP tables are initialized only for policy->cpu, do it for
@@ -380,6 +383,8 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
if (data && data->have_governor_per_policy)
dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
+ dt_cpufreq_driver.driver_data = data;
+
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)
dev_err(&pdev->dev, "failed register driver: %d\n", ret);
diff --git a/drivers/cpufreq/cpufreq-dt.h b/drivers/cpufreq/cpufreq-dt.h
index 54d774e..dcc03c6 100644
--- a/drivers/cpufreq/cpufreq-dt.h
+++ b/drivers/cpufreq/cpufreq-dt.h
@@ -13,6 +13,12 @@
#include <linux/types.h>
struct cpufreq_dt_platform_data {
+ /*
+ * True when each CPU has its own clock to control its
+ * frequency, false when all CPUs are controlled by a single
+ * clock.
+ */
+ bool independent_clocks;
bool have_governor_per_policy;
};
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 11/12] cpufreq: Add module to register cpufreq on Krait CPUs
From: Sricharan R @ 2017-12-08 9:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512725401-4944-1-git-send-email-sricharan@codeaurora.org>
From: Stephen Boyd <sboyd@codeaurora.org>
Register a cpufreq-generic device whenever we detect that a
"qcom,krait" compatible CPU is present in DT.
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++
drivers/cpufreq/Kconfig.arm | 9 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++
4 files changed, 252 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
create mode 100644 drivers/cpufreq/qcom-cpufreq.c
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
new file mode 100644
index 0000000..e7cb104
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
@@ -0,0 +1,38 @@
+Qualcomm Process Voltage Scaling Tables
+
+The node name is required to be "qcom,pvs". There shall only be one
+such node present in the root of the tree.
+
+PROPERTIES
+
+- qcom,pvs-format-a or qcom,pvs-format-b:
+ Usage: required
+ Value type: <empty>
+ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
+ If qcom,pvs-format-a is used the table is two columns
+ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage,
+ and current in that order).
+
+- qcom,speedX-pvsY-bin-vZ:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
+ and version Z.
+Example:
+
+ qcom,pvs {
+ qcom,pvs-format-a;
+ qcom,speed0-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 975000 >,
+ < 594000000 1000000 >,
+ < 702000000 1025000 >,
+ < 810000000 1075000 >,
+ < 918000000 1100000 >,
+ < 1026000000 1125000 >,
+ < 1134000000 1175000 >,
+ < 1242000000 1200000 >,
+ < 1350000000 1225000 >,
+ < 1458000000 1237500 >,
+ < 1512000000 1250000 >;
+ };
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index bdce448..60f28e7 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -100,6 +100,15 @@ config ARM_OMAP2PLUS_CPUFREQ
depends on ARCH_OMAP2PLUS
default ARCH_OMAP2PLUS
+config ARM_QCOM_CPUFREQ
+ tristate "Qualcomm based"
+ depends on ARCH_QCOM
+ select PM_OPP
+ help
+ This adds the CPUFreq driver for Qualcomm SoC based boards.
+
+ If in doubt, say N.
+
config ARM_S3C_CPUFREQ
bool
help
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 812f9e0..1496464 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
new file mode 100644
index 0000000..95ed2ba
--- /dev/null
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -0,0 +1,204 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include "cpufreq-dt.h"
+
+static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
+{
+ void __iomem *base;
+ u32 pte_efuse;
+
+ *speed = *pvs = *pvs_ver = 0;
+
+ base = ioremap(0x007000c0, 4);
+ if (!base) {
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
+ return;
+ }
+
+ pte_efuse = readl_relaxed(base);
+ iounmap(base);
+
+ *speed = pte_efuse & 0xf;
+ if (*speed == 0xf)
+ *speed = (pte_efuse >> 4) & 0xf;
+
+ if (*speed == 0xf) {
+ *speed = 0;
+ pr_warn("Speed bin: Defaulting to %d\n", *speed);
+ } else {
+ pr_info("Speed bin: %d\n", *speed);
+ }
+
+ *pvs = (pte_efuse >> 10) & 0x7;
+ if (*pvs == 0x7)
+ *pvs = (pte_efuse >> 13) & 0x7;
+
+ if (*pvs == 0x7) {
+ *pvs = 0;
+ pr_warn("PVS bin: Defaulting to %d\n", *pvs);
+ } else {
+ pr_info("PVS bin: %d\n", *pvs);
+ }
+}
+
+static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
+{
+ u32 pte_efuse, redundant_sel;
+ void __iomem *base;
+
+ *speed = 0;
+ *pvs = 0;
+ *pvs_ver = 0;
+
+ base = ioremap(0xfc4b80b0, 8);
+ if (!base) {
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
+ return;
+ }
+
+ pte_efuse = readl_relaxed(base);
+ redundant_sel = (pte_efuse >> 24) & 0x7;
+ *speed = pte_efuse & 0x7;
+ /* 4 bits of PVS are in efuse register bits 31, 8-6. */
+ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
+ *pvs_ver = (pte_efuse >> 4) & 0x3;
+
+ switch (redundant_sel) {
+ case 1:
+ *speed = (pte_efuse >> 27) & 0xf;
+ break;
+ case 2:
+ *pvs = (pte_efuse >> 27) & 0xf;
+ break;
+ }
+
+ /* Check SPEED_BIN_BLOW_STATUS */
+ if (pte_efuse & BIT(3)) {
+ pr_info("Speed bin: %d\n", *speed);
+ } else {
+ pr_warn("Speed bin not set. Defaulting to 0!\n");
+ *speed = 0;
+ }
+
+ /* Check PVS_BLOW_STATUS */
+ pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
+ if (pte_efuse) {
+ pr_info("PVS bin: %d\n", *pvs);
+ } else {
+ pr_warn("PVS bin not set. Defaulting to 0!\n");
+ *pvs = 0;
+ }
+
+ pr_info("PVS version: %d\n", *pvs_ver);
+ iounmap(base);
+}
+
+static int __init qcom_cpufreq_populate_opps(void)
+{
+ int len, rows, cols, i, k, speed, pvs, pvs_ver;
+ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
+ struct device_node *np;
+ struct device *dev;
+ int cpu = 0;
+
+ np = of_find_node_by_name(NULL, "qcom,pvs");
+ if (!np)
+ return -ENODEV;
+
+ if (of_property_read_bool(np, "qcom,pvs-format-a")) {
+ get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
+ cols = 2;
+ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
+ get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
+ cols = 3;
+ } else {
+ return -ENODEV;
+ }
+
+ snprintf(table_name, sizeof(table_name),
+ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
+
+ if (!of_find_property(np, table_name, &len))
+ return -EINVAL;
+
+ len /= sizeof(u32);
+ if (len % cols || len == 0)
+ return -EINVAL;
+
+ rows = len / cols;
+
+ for (i = 0, k = 0; i < rows; i++) {
+ u32 freq, volt;
+
+ of_property_read_u32_index(np, table_name, k++, &freq);
+ of_property_read_u32_index(np, table_name, k++, &volt);
+ while (k % cols)
+ k++; /* Skip uA entries if present */
+ for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
+ dev = get_cpu_device(cpu);
+ if (!dev)
+ return -ENODEV;
+ if (dev_pm_opp_add(dev, freq, volt))
+ pr_warn("failed to add OPP %u\n", freq);
+ }
+ }
+
+ return 0;
+}
+
+static int __init qcom_cpufreq_driver_init(void)
+{
+ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
+ struct platform_device_info devinfo = {
+ .name = "cpufreq-dt",
+ .data = &pdata,
+ .size_data = sizeof(pdata),
+ };
+ struct device *cpu_dev;
+ struct device_node *np;
+ int ret;
+
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev)
+ return -ENODEV;
+
+ np = of_node_get(cpu_dev->of_node);
+ if (!np)
+ return -ENOENT;
+
+ if (!of_device_is_compatible(np, "qcom,krait")) {
+ of_node_put(np);
+ return -ENODEV;
+ }
+ of_node_put(np);
+
+ ret = qcom_cpufreq_populate_opps();
+ if (ret)
+ return ret;
+
+ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
+}
+module_init(qcom_cpufreq_driver_init);
+
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
+MODULE_LICENSE("GPL v2");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v4 10/12] clk: qcom: Add safe switch hook for krait mux clocks
From: Sricharan R @ 2017-12-08 9:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512725401-4944-1-git-send-email-sricharan@codeaurora.org>
When the Hfplls are reprogrammed during the rate change,
the primary muxes which are sourced from the same hfpll
for higher frequencies, needs to be switched to the 'safe
secondary mux' as the parent for that small window. This
is done by registering a clk notifier for the muxes and
switching to the safe parent in the PRE_RATE_CHANGE notifier
and back to the original parent in the POST_RATE_CHANGE notifier.
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
---
drivers/clk/qcom/clk-krait.c | 2 ++
drivers/clk/qcom/clk-krait.h | 3 +++
drivers/clk/qcom/krait-cc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c
index 6099307..1627a6e 100644
--- a/drivers/clk/qcom/clk-krait.c
+++ b/drivers/clk/qcom/clk-krait.c
@@ -60,6 +60,8 @@ static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
if (__clk_is_enabled(hw->clk))
__krait_mux_set_sel(mux, sel);
+ mux->reparent = true;
+
return 0;
}
diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h
index af89782..f9e1279 100644
--- a/drivers/clk/qcom/clk-krait.h
+++ b/drivers/clk/qcom/clk-krait.h
@@ -23,6 +23,9 @@ struct krait_mux_clk {
u32 shift;
u32 en_mask;
bool lpl;
+ u8 safe_sel;
+ u8 old_index;
+ bool reparent;
struct clk_hw hw;
struct notifier_block clk_nb;
diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c
index f5ffb1a..ec899ad 100644
--- a/drivers/clk/qcom/krait-cc.c
+++ b/drivers/clk/qcom/krait-cc.c
@@ -35,6 +35,49 @@
0,
};
+/*
+ * Notifier function for switching the muxes to safe parent
+ * while the hfpll is getting reprogrammed.
+ */
+static int krait_notifier_cb(struct notifier_block *nb,
+ unsigned long event,
+ void *data)
+{
+ int ret = 0;
+ struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk,
+ clk_nb);
+ /* Switch to safe parent */
+ if (event == PRE_RATE_CHANGE) {
+ mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw);
+ ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel);
+ mux->reparent = false;
+ /*
+ * By the time POST_RATE_CHANGE notifier is called,
+ * clk framework itself would have changed the parent for the new rate.
+ * Only otherwise, put back to the old parent.
+ */
+ } else if (event == POST_RATE_CHANGE) {
+ if (!mux->reparent)
+ ret = krait_mux_clk_ops.set_parent(&mux->hw,
+ mux->old_index);
+ }
+
+ return notifier_from_errno(ret);
+}
+
+static int krait_notifier_register(struct device *dev, struct clk *clk,
+ struct krait_mux_clk *mux)
+{
+ int ret = 0;
+
+ mux->clk_nb.notifier_call = krait_notifier_cb;
+ ret = clk_notifier_register(clk, &mux->clk_nb);
+ if (ret)
+ dev_err(dev, "failed to register clock notifier: %d\n", ret);
+
+ return ret;
+}
+
static int
krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
{
@@ -79,6 +122,7 @@
krait_add_sec_mux(struct device *dev, int id, const char *s,
unsigned int offset, bool unique_aux)
{
+ int ret;
struct krait_mux_clk *mux;
static const char *sec_mux_list[] = {
"acpu_aux",
@@ -102,6 +146,7 @@
mux->shift = 2;
mux->parent_map = sec_mux_map;
mux->hw.init = &init;
+ mux->safe_sel = 0;
init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
if (!init.name)
@@ -117,6 +162,11 @@
clk = devm_clk_register(dev, &mux->hw);
+ ret = krait_notifier_register(dev, clk, mux);
+ if (ret)
+ goto unique_aux;
+
+unique_aux:
if (unique_aux)
kfree(sec_mux_list[0]);
err_aux:
@@ -128,6 +178,7 @@
krait_add_pri_mux(struct device *dev, int id, const char *s,
unsigned int offset)
{
+ int ret;
struct krait_mux_clk *mux;
const char *p_names[3];
struct clk_init_data init = {
@@ -148,6 +199,7 @@
mux->lpl = id >= 0;
mux->parent_map = pri_mux_map;
mux->hw.init = &init;
+ mux->safe_sel = 2;
init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
if (!init.name)
@@ -173,6 +225,10 @@
clk = devm_clk_register(dev, &mux->hw);
+ ret = krait_notifier_register(dev, clk, mux);
+ if (ret)
+ goto err_p3;
+err_p3:
kfree(p_names[2]);
err_p2:
kfree(p_names[1]);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox