* [PATCH 01/22] dt-bindings: iio: adc: add AXP20X/AXP22X ADC DT binding
From: Maxime Ripard @ 2017-01-05 16:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170102163723.7939-2-quentin.schulz@free-electrons.com>
On Mon, Jan 02, 2017 at 05:37:01PM +0100, Quentin Schulz wrote:
> The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
> battery voltage, battery charge and discharge currents, AC-in and VBUS
> voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
>
> This adds the device tree binding documentation for the X-Powers AXP20X
> and AXP22X PMICs ADCs.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Thanks,
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/cd9e296a/attachment.sig>
^ permalink raw reply
* [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
From: Maxime Ripard @ 2017-01-05 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cf4590d1-5381-f1da-7271-e6e7fee0c479@free-electrons.com>
On Thu, Jan 05, 2017 at 10:50:33AM +0100, Quentin Schulz wrote:
> >>>> +static int axp20x_remove(struct platform_device *pdev)
> >>>> +{
> >>>> + struct axp20x_adc_iio *info;
> >>>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >>>> +
> >>>> + info = iio_priv(indio_dev);
> >>>
> >>> Nit: you could just reverse the 2 declarations above and join this
> >>> line after struct axp20x_adc_iio *info;
> >>>
> >>>> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> >>>> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> >>>
> >>> The existing VBUS power supply driver enables the VBUS ADC bits itself,
> >>> and does not check them later on. This means if one were to remove this
> >>> axp20x-adc module, the voltage/current readings in the VBUS power supply
> >>> would be invalid. Some sort of workaround would be needed here in this
> >>> driver of the VBUS driver.
> >>>
> >>
> >> That would be one reason to migrate the VBUS driver to use the IIO
> >> channels, wouldn't it?
> >
> > It is, preferably without changing the device tree.
> >
>
> Yes, of course.
Note that you can have something like a test for the iio channel
property in the VBUS driver, and keep the old behaviour in the case
where we wouldn't have it.
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/1230184d/attachment.sig>
^ permalink raw reply
* [PATCH V9 3/3] irqchip: qcom: Add IRQ combiner driver
From: Marc Zyngier @ 2017-01-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481753438-3905-4-git-send-email-agustinv@codeaurora.org>
Hi Agustin,
On 14/12/16 22:10, Agustin Vega-Frias wrote:
> Driver for interrupt combiners in the Top-level Control and Status
> Registers (TCSR) hardware block in Qualcomm Technologies chips.
>
> An interrupt combiner in this block combines a set of interrupts by
> OR'ing the individual interrupt signals into a summary interrupt
> signal routed to a parent interrupt controller, and provides read-
> only, 32-bit registers to query the status of individual interrupts.
> The status bit for IRQ n is bit (n % 32) within register (n / 32)
> of the given combiner. Thus, each combiner can be described as a set
> of register offsets and the number of IRQs managed.
>
> Signed-off-by: Agustin Vega-Frias <agustinv@codeaurora.org>
> ---
> drivers/irqchip/Kconfig | 9 +
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/qcom-irq-combiner.c | 322 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 332 insertions(+)
> create mode 100644 drivers/irqchip/qcom-irq-combiner.c
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index bc0af33..3e3430c 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -279,3 +279,12 @@ config EZNPS_GIC
> config STM32_EXTI
> bool
> select IRQ_DOMAIN
> +
> +config QCOM_IRQ_COMBINER
> + bool "QCOM IRQ combiner support"
> + depends on ARCH_QCOM && ACPI
> + select IRQ_DOMAIN
> + select IRQ_DOMAIN_HIERARCHY
> + help
> + Say yes here to add support for the IRQ combiner devices embedded
> + in Qualcomm Technologies chips.
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index e4dbfc8..1818a0b 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
> obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
> obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
> obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
> +obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
> diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c
> new file mode 100644
> index 0000000..0055e08
> --- /dev/null
> +++ b/drivers/irqchip/qcom-irq-combiner.c
> @@ -0,0 +1,322 @@
> +/* Copyright (c) 2015-2016, 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.
> + */
> +
> +/*
> + * Driver for interrupt combiners in the Top-level Control and Status
> + * Registers (TCSR) hardware block in Qualcomm Technologies chips.
> + * An interrupt combiner in this block combines a set of interrupts by
> + * OR'ing the individual interrupt signals into a summary interrupt
> + * signal routed to a parent interrupt controller, and provides read-
> + * only, 32-bit registers to query the status of individual interrupts.
> + * The status bit for IRQ n is bit (n % 32) within register (n / 32)
> + * of the given combiner. Thus, each combiner can be described as a set
> + * of register offsets and the number of IRQs managed.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/platform_device.h>
> +
> +#define REG_SIZE 32
> +
> +struct combiner_reg {
> + void __iomem *addr;
> + unsigned long mask;
> +};
> +
> +struct combiner {
> + struct irq_domain *domain;
> + int parent_irq;
> + u32 nirqs;
> + u32 nregs;
> + struct combiner_reg regs[0];
> +};
> +
> +static inline u32 irq_register(int irq)
> +{
> + return irq / REG_SIZE;
> +}
> +
> +static inline u32 irq_bit(int irq)
> +{
> + return irq % REG_SIZE;
> +
> +}
> +
> +static inline int irq_nr(u32 reg, u32 bit)
> +{
> + return reg * REG_SIZE + bit;
> +}
> +
> +/*
> + * Handler for the cascaded IRQ.
> + */
> +static void combiner_handle_irq(struct irq_desc *desc)
> +{
> + struct combiner *combiner = irq_desc_get_handler_data(desc);
> + struct irq_chip *chip = irq_desc_get_chip(desc);
> + u32 reg;
> +
> + chained_irq_enter(chip, desc);
> +
> + for (reg = 0; reg < combiner->nregs; reg++) {
> + int virq;
> + int hwirq;
> + u32 bit;
> + u32 status;
> +
> + if (combiner->regs[reg].mask == 0)
> + continue;
I'm a bit worried by this. If I understand it well, this is a pure
software construct (controlled from combiner_irq_chip_{un,}mask_irq) and
there is nothing that actually masks the interrupt at the HW level.
So if a device asserts its interrupt line, what mechanism do we have to
make sure that we don't end-up with the CPU pegged in interrupt context?
> +
> + status = readl_relaxed(combiner->regs[reg].addr);
> + status &= combiner->regs[reg].mask;
> +
> + while (status) {
> + bit = __ffs(status);
> + status &= ~(1 << bit);
> + hwirq = irq_nr(reg, bit);
> + virq = irq_find_mapping(combiner->domain, hwirq);
> + if (virq >= 0)
Small bug: virq == 0 shouldn't happen, since this would be an indication
that the hwirq doesn't have a mapping.
> + generic_handle_irq(virq);
> +
> + }
> + }
> +
> + chained_irq_exit(chip, desc);
> +}
> +
> +/*
> + * irqchip callbacks
> + */
> +
> +static void combiner_irq_chip_mask_irq(struct irq_data *data)
> +{
> + struct combiner *combiner = irq_data_get_irq_chip_data(data);
> + struct combiner_reg *reg = combiner->regs + irq_register(data->hwirq);
> +
> + clear_bit(irq_bit(data->hwirq), ®->mask);
> +}
> +
> +static void combiner_irq_chip_unmask_irq(struct irq_data *data)
> +{
> + struct combiner *combiner = irq_data_get_irq_chip_data(data);
> + struct combiner_reg *reg = combiner->regs + irq_register(data->hwirq);
> +
> + set_bit(irq_bit(data->hwirq), ®->mask);
> +}
> +
> +static struct irq_chip irq_chip = {
> + .irq_mask = combiner_irq_chip_mask_irq,
> + .irq_unmask = combiner_irq_chip_unmask_irq,
> + .name = "qcom-irq-combiner"
> +};
> +
> +/*
> + * irq_domain_ops callbacks
> + */
> +
> +static int combiner_irq_map(struct irq_domain *domain, unsigned int irq,
> + irq_hw_number_t hwirq)
> +{
> + struct combiner *combiner = domain->host_data;
> +
> + if (hwirq >= combiner->nirqs)
> + return -EINVAL;
Given that this should have come from the translate function, can we
move the check there instead, so that we validate everything early?
> +
> + irq_set_chip_and_handler(irq, &irq_chip, handle_level_irq);
> + irq_set_chip_data(irq, combiner);
> + irq_set_noprobe(irq);
> + return 0;
> +}
> +
> +static void combiner_irq_unmap(struct irq_domain *domain, unsigned int irq)
> +{
> + struct irq_data *data = irq_get_irq_data(irq);
> +
> + if (WARN_ON(!data))
> + return;
Can this happen?
> + irq_domain_reset_irq_data(data);
> +}
> +
> +static int combiner_irq_translate(struct irq_domain *d, struct irq_fwspec *fws,
> + unsigned long *hwirq, unsigned int *type)
> +{
> + if (is_acpi_node(fws->fwnode)) {
> + if (WARN_ON((fws->param_count != 2) ||
> + (fws->param[1] & IORESOURCE_IRQ_LOWEDGE) ||
> + (fws->param[1] & IORESOURCE_IRQ_HIGHEDGE)))
> + return -EINVAL;
> +
> + *hwirq = fws->param[0];
> + *type = fws->param[1];
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct irq_domain_ops domain_ops = {
> + .map = combiner_irq_map,
> + .unmap = combiner_irq_unmap,
> + .translate = combiner_irq_translate
> +};
> +
> +/*
> + * Device probing
> + */
> +
> +static acpi_status count_registers_cb(struct acpi_resource *ares, void *context)
> +{
> + int *count = context;
> +
> + if (ares->type == ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
> + ++(*count);
> + return AE_OK;
> +}
> +
> +static int count_registers(struct platform_device *pdev)
> +{
> + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
> + acpi_status status;
> + int count = 0;
> +
> + if (!acpi_has_method(adev->handle, METHOD_NAME__CRS))
> + return -EINVAL;
> +
> + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
> + count_registers_cb, &count);
> + if (ACPI_FAILURE(status))
> + return -EINVAL;
> + return count;
> +}
> +
> +struct get_registers_context {
> + struct device *dev;
> + struct combiner *combiner;
> + int err;
> +};
> +
> +static acpi_status get_registers_cb(struct acpi_resource *ares, void *context)
> +{
> + struct get_registers_context *ctx = context;
> + struct acpi_resource_generic_register *reg;
> + phys_addr_t paddr;
> + void __iomem *vaddr;
> +
> + if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
> + return AE_OK;
> +
> + reg = &ares->data.generic_reg;
> + paddr = reg->address;
> + if ((reg->space_id != ACPI_SPACE_MEM) ||
> + (reg->bit_offset != 0) ||
> + (reg->bit_width > REG_SIZE)) {
> + dev_err(ctx->dev, "Bad register resource @%pa\n", &paddr);
> + ctx->err = -EINVAL;
> + return AE_ERROR;
> + }
> +
> + vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE);
> + if (IS_ERR(vaddr)) {
> + dev_err(ctx->dev, "Can't map register @%pa\n", &paddr);
> + ctx->err = PTR_ERR(vaddr);
> + return AE_ERROR;
> + }
> +
> + ctx->combiner->regs[ctx->combiner->nregs].addr = vaddr;
> + ctx->combiner->nirqs += reg->bit_width;
> + ctx->combiner->nregs++;
> + return AE_OK;
> +}
> +
> +static int get_registers(struct platform_device *pdev, struct combiner *comb)
> +{
> + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
> + acpi_status status;
> + struct get_registers_context ctx;
> +
> + if (!acpi_has_method(adev->handle, METHOD_NAME__CRS))
> + return -EINVAL;
> +
> + ctx.dev = &pdev->dev;
> + ctx.combiner = comb;
> + ctx.err = 0;
> +
> + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
> + get_registers_cb, &ctx);
> + if (ACPI_FAILURE(status))
> + return ctx.err;
> + return 0;
> +}
> +
> +static int __init combiner_probe(struct platform_device *pdev)
> +{
> + struct combiner *combiner;
> + size_t alloc_sz;
> + u32 nregs;
> + int err;
> +
> + nregs = count_registers(pdev);
> + if (nregs <= 0) {
> + dev_err(&pdev->dev, "Error reading register resources\n");
> + return -EINVAL;
> + }
> +
> + alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * nregs;
> + combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL);
> + if (!combiner)
> + return -ENOMEM;
> +
> + err = get_registers(pdev, combiner);
> + if (err < 0)
> + return err;
> +
> + combiner->parent_irq = platform_get_irq(pdev, 0);
> + if (combiner->parent_irq <= 0) {
> + dev_err(&pdev->dev, "Error getting IRQ resource\n");
> + return -EPROBE_DEFER;
> + }
> +
> + combiner->domain = irq_domain_create_linear(pdev->dev.fwnode, combiner->nirqs,
> + &domain_ops, combiner);
> + if (!combiner->domain)
> + /* Errors printed by irq_domain_create_linear */
> + return -ENODEV;
> +
> + irq_set_chained_handler_and_data(combiner->parent_irq,
> + combiner_handle_irq, combiner);
> +
> + dev_info(&pdev->dev, "Initialized with [p=%d,n=%d,r=%p]\n",
> + combiner->parent_irq, combiner->nirqs, combiner->regs[0].addr);
> + return 0;
> +}
> +
> +static const struct acpi_device_id qcom_irq_combiner_acpi_match[] = {
> + { "QCOM80B1", },
> + { }
> +};
> +
> +static struct platform_driver qcom_irq_combiner_probe = {
> + .driver = {
> + .name = "qcom-irq-combiner",
> + .owner = THIS_MODULE,
> + .acpi_match_table = ACPI_PTR(qcom_irq_combiner_acpi_match),
> + },
> + .probe = combiner_probe,
> +};
> +
> +static int __init register_qcom_irq_combiner(void)
> +{
> + return platform_driver_register(&qcom_irq_combiner_probe);
> +}
> +device_initcall(register_qcom_irq_combiner);
>
Other than the questions I have above, this now looks in a much better
shape. Hopefully Rafael will soon come back will his conclusions on the
first two patches, as I'd like to see this code to get some -next for a
while.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
From: Maxime Ripard @ 2017-01-05 16:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170102163723.7939-4-quentin.schulz@free-electrons.com>
On Mon, Jan 02, 2017 at 05:37:03PM +0100, Quentin Schulz wrote:
> + switch (axp20x_id) {
> + case AXP209_ID:
> + indio_dev->info = &axp20x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp20x_adc_channels);
> + indio_dev->channels = axp20x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP20X_ADC_EN1_MASK);
> +
> + /* Enable GPIO0/1 and internal temperature ADCs */
> + regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP20X_ADC_RATE_50HZ);
> + break;
> +
> + case AXP221_ID:
> + indio_dev->info = &axp22x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp22x_adc_channels);
> + indio_dev->channels = axp22x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP22X_ADC_EN1_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP22X_ADC_RATE_200HZ);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
I'm not a big fan of those IDs. It always ends up with a ever
increasing list of cases without any consolidation.
In this case, the only thing you need is a list of pointers and values
that can definitely be stored in a structure.
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/c5ff1884/attachment.sig>
^ permalink raw reply
* [PATCH v2 0/3] ARM: dts: dra7-evm: pinmuxing updates
From: Tony Lindgren @ 2017-01-05 16:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1481628266.git.nsekhar@ti.com>
* Sekhar Nori <nsekhar@ti.com> [161213 03:30]:
> Hi,
>
> This patch series drops most of the pinmuxing from dra7-evm
> to satisfy erratum i869 and adds missing eMMC and SD card pinmuxing.
>
> The patches apply to latest linux-next. Tested on DRA74x EVM Rev H
> using U-Boot 2016.11. Boot tested using NFS and basic testing carried
> out on SD card, eMMC, Audio capture and playback.
>
> NAND on DRA72 EVM is already unusable due to a similar patch. A patch
> included here disables the GPMC node.
>
> v1 -> v2:
> - Add a patch to disable GPMC node on DRA72x EVM
> - Disable GPMC node on DRA74x EVM
>
> Kishon Vijay Abraham I (1):
> ARM: dts: dra7-evm: add pinmux configuration for mmc1/2
>
> Sekhar Nori (2):
> ARM: dts: dra7-evm: Remove pinmux configurations for erratum i869
> ARM: dts: dra72-evm: drop NAND support
>
> arch/arm/boot/dts/dra7-evm.dts | 282 ++++----------------------------
> arch/arm/boot/dts/dra72-evm-common.dtsi | 7 +-
> 2 files changed, 35 insertions(+), 254 deletions(-)
Applying all into omap-for-v4.11/dt thanks.
Tony
^ permalink raw reply
* [PATCH 17/22] power: supply: add battery driver for AXP20X and AXP22X PMICs
From: Maxime Ripard @ 2017-01-05 17:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170102163723.7939-18-quentin.schulz@free-electrons.com>
On Mon, Jan 02, 2017 at 05:37:17PM +0100, Quentin Schulz wrote:
> + /*
> + * IIO framework gives mV but Power Supply framework gives ?V.
> + */
> + val->intval *= 1000;
s/gives/wants/ ?
> +static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> + int val)
> +{
> + switch (val) {
> + case 4100000:
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP20X_CHRG_CTRL1,
> + AXP20X_CHRG_CTRL1_TGT_VOLT,
> + AXP20X_CHRG_CTRL1_TGT_4_1V);
> + case 4150000:
> + if (axp20x_batt->axp_id == AXP221_ID)
> + return -EINVAL;
> +
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP20X_CHRG_CTRL1,
> + AXP20X_CHRG_CTRL1_TGT_VOLT,
> + AXP20X_CHRG_CTRL1_TGT_4_15V);
> + case 4200000:
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP20X_CHRG_CTRL1,
> + AXP20X_CHRG_CTRL1_TGT_VOLT,
> + AXP20X_CHRG_CTRL1_TGT_4_2V);
> + default:
> + /*
> + * AXP20x max voltage can be set to 4.36V and AXP22X max voltage
> + * can be set to 4.22V and 4.24V, but these voltages are too
> + * high for Lithium based batteries (AXP PMICs are supposed to
> + * be used with these kinds of battery).
> + */
> + return -EINVAL;
> + }
Since all your calls to regmap are the same, something like:
case 4100000:
val = AXP20X_CHRG_CTRL1_TGT_4_1V;
break;
case 4150000:
val = AXP20X_CHRG_CTRL1_TGT_4_15V;
break;
regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1,
AXP20X_CHRG_CTRL1_TGT_VOLT, val);
It would also get rid of your warnings in checkpatch.
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/e854f1cc/attachment.sig>
^ permalink raw reply
* [PATCH v3 1/9] arm64: cpufeature: treat unknown fields as RES0
From: Catalin Marinas @ 2017-01-05 17:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483552147-9605-2-git-send-email-suzuki.poulose@arm.com>
On Wed, Jan 04, 2017 at 05:48:59PM +0000, Suzuki K. Poulose wrote:
> From: Mark Rutland <mark.rutland@arm.com>
>
> Any fields not defined in an arm64_ftr_bits entry are propagated to the
> system-wide register value in init_cpu_ftr_reg(), and while we require
> that these strictly match for the sanity checks, we don't update them in
> update_cpu_ftr_reg().
>
> Generally, the lack of an arm64_ftr_bits entry indicates that the bits
> are currently RES0 (as is the case for the upper 32 bits of all
> supposedly 32-bit registers).
>
> A better default would be to use zero for the system-wide value of
> unallocated bits, making all register checking consistent, and allowing
> for subsequent simplifications to the arm64_ftr_bits arrays.
>
> This patch updates init_cpu_ftr_reg() to treat unallocated bits as RES0
> for the purpose of the system-wide safe value. These bits will still be
> sanity checked with strict match requirements, as is currently the case.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* [PATCH v3 2/9] arm64: cpufeature: remove explicit RAZ fields
From: Catalin Marinas @ 2017-01-05 17:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483552147-9605-3-git-send-email-suzuki.poulose@arm.com>
On Wed, Jan 04, 2017 at 05:49:00PM +0000, Suzuki K. Poulose wrote:
> From: Mark Rutland <mark.rutland@arm.com>
>
> We currently have some RAZ fields described explicitly in our
> arm64_ftr_bits arrays. These are inconsistently commented, grouped,
> and/or applied, and maintaining these is error-prone.
>
> Luckily, we don't need these at all. We'll never need to inspect RAZ
> fields to determine feature support, and init_cpu_ftr_reg() will ensure
> that any bits without a corresponding arm64_ftr_bits entry are treated
> as RES0 with strict matching requirements. In check_update_ftr_reg()
> we'll then compare these bits from the relevant cpuinfo_arm64
> structures, and need not store them in a arm64_ftr_reg.
>
> This patch removes the unnecessary arm64_ftr_bits entries for RES0 bits.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* [PATCH v2 4/4] ARM: dts: keystone: Add "ti, da830-uart" compatible string
From: Santosh Shilimkar @ 2017-01-05 17:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <824238e0-b710-7146-b67b-b2135c2a8bd2@ti.com>
On 1/5/2017 1:04 AM, Sekhar Nori wrote:
> Hi Santosh,
>
> On Thursday 05 January 2017 03:30 AM, Santosh Shilimkar wrote:
>> On 1/4/2017 12:30 PM, David Lechner wrote:
>>> The TI Keystone SoCs have extra UART registers beyond the standard 8250
>>> registers, so we need a new compatible string to indicate this. Also, at
>>> least one of these registers uses the full 32 bits, so we need to specify
>>> reg-io-width in addition to reg-shift.
>>>
>>> "ns16550a" is left in the compatible specification since it does work as
>>> long as the bootloader configures the SoC UART power management
>>> registers.
>>>
>> NAK!!
>> We can't break the booting boards with existing boot loaders.
>
> Sorry, but it not clear to me how this breaks booting with older
> bootloaders? If older DTB is ROM'ed, it will continue to work because of
> match with ns16550a.
>
> I just verified boot on K2E with these patches applied and using 2016.05
> based U-Boot from a TI release.
>
> http://pastebin.ubuntu.com/23744719/
>
Thanks for test. As long as it doesn't break the boot, am fine
with it.
>> I suggest you to first get the driver updated to take care of
>> the UART PM register and then enable the support for it.
>
> Isn't that what patch 2/4 is doing?
>
I see that now. Thanks for clarifying.
Serial patch needs to go via Greg's tree. I will pick up the
DTS bits.
Regards,
Santosh
^ permalink raw reply
* [PATCH 2/2] media: rc: add driver for IR remote receiver on MT7623 SoC
From: Sean Young @ 2017-01-05 17:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483632384-8107-3-git-send-email-sean.wang@mediatek.com>
Hi Sean,
Some review comments.
On Fri, Jan 06, 2017 at 12:06:24AM +0800, sean.wang at mediatek.com wrote:
> From: Sean Wang <sean.wang@mediatek.com>
>
> This patch adds driver for IR controller on
> Mediatek MT7623 SoC. Currently testing successfully
> on NEC and SONY remote controller only but it should
> work on others (lirc, rc-5 and rc-6).
>
> Signed-off-by: Sean Wang <sean.wang@mediatek.com>
> ---
> drivers/media/rc/Kconfig | 10 ++
> drivers/media/rc/Makefile | 1 +
> drivers/media/rc/mtk-cir.c | 319 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 330 insertions(+)
> create mode 100644 linux-4.8.rc1_p0/drivers/media/rc/mtk-cir.c
>
> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> index 370e16e..626c500 100644
> --- a/drivers/media/rc/Kconfig
> +++ b/drivers/media/rc/Kconfig
> @@ -389,4 +389,14 @@ config IR_SUNXI
> To compile this driver as a module, choose M here: the module will
> be called sunxi-ir.
>
> +config IR_MTK
> + tristate "Mediatek IR remote control"
> + depends on RC_CORE
> + depends on ARCH_MEDIATEK || COMPILE_TEST
> + ---help---
> + Say Y if you want to use Mediatek internal IR Controller
> +
> + To compile this driver as a module, choose M here: the module will
> + be called mtk-cir.
> +
> endif #RC_DEVICES
> diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> index 379a5c0..505908d 100644
> --- a/drivers/media/rc/Makefile
> +++ b/drivers/media/rc/Makefile
> @@ -37,3 +37,4 @@ obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
> obj-$(CONFIG_RC_ST) += st_rc.o
> obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o
> obj-$(CONFIG_IR_IMG) += img-ir/
> +obj-$(CONFIG_IR_MTK) += mtk-cir.o
> diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c
> new file mode 100644
> index 0000000..4fa4cab
> --- /dev/null
> +++ b/drivers/media/rc/mtk-cir.c
> @@ -0,0 +1,319 @@
> +/*
> + * Driver for Mediatek MT7623 IR Receiver Controller
> + *
> + * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * 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/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/reset.h>
> +#include <media/rc-core.h>
> +
> +#define MTK_IR_DEV "mtk-ir"
KBUILD_MODNAME could be used instead. Currently the module is called
mtk-cir but the rc device will have driver name mtk-ir.
> +
> +/* Register to enable PWM and IR */
> +#define MTK_CONFIG_HIGH_REG 0x0c
> +/* Enable IR pulse width detection */
> +#define MTK_PWM_EN BIT(13)
> +/* Enable IR hardware function */
> +#define MTK_IR_EN BIT(0)
> +
> +/* Register to setting sample period */
> +#define MTK_CONFIG_LOW_REG 0x10
> +/* Field to set sample period */
> +#define CHK_PERIOD 0xC00
> +#define MTK_CHK_PERIOD (((CHK_PERIOD) << 8) & (GENMASK(20, 8)))
> +#define MTK_CHK_PERIOD_MASK (GENMASK(20, 8))
> +
> +/* Register to clear state of state machine */
> +#define MTK_IRCLR_REG 0x20
> +/* Bit to restart IR receiving */
> +#define MTK_IRCLR BIT(0)
> +
> +/* Register containing pulse width data */
> +#define MTK_CHKDATA_REG(i) (0x88 + 4 * i)
> +
> +/* Register to enable IR interrupt */
> +#define MTK_IRINT_EN_REG 0xcc
> +/* Bit to enable interrupt */
> +#define MTK_IRINT_EN BIT(0)
> +
> +/* Register to ack IR interrupt */
> +#define MTK_IRINT_CLR_REG 0xd0
> +/* Bit to clear interrupt status */
> +#define MTK_IRINT_CLR BIT(0)
> +
> +/* Number of registers to record the pulse width */
> +#define MTK_CHKDATA_SZ 17
> +/* Required frequency */
> +#define MTK_IR_BASE_CLK 273000000
> +/* Frequency after IR internal divider */
> +#define MTK_IR_CLK (MTK_IR_BASE_CLK / 4)
> +/* Sample period in ns */
> +#define MTK_IR_SAMPLE (((1000000000ul / MTK_IR_CLK) * CHK_PERIOD))
> +/* Indicate the end of IR data*/
> +#define MTK_IR_END(v) (v == 0xff)
> +
> +/* struct mtk_ir - This is the main datasructure for holding the state
> + * of the driver
> + * @dev: The device pointer
> + * @ir_lock: Make sure that synchronization between remove and ISR
> + * @rc: The rc instrance
> + * @base: The mapped register i/o base
> + * @irq: The IRQ that we are using
> + * @clk: The clock that we are using
> + * @map_name: The name for keymap lookup
> + */
> +struct mtk_ir {
> + struct device *dev;
> + /*Protect concurrency between driver removal and ISR*/
> + spinlock_t ir_lock;
> + struct rc_dev *rc;
> + void __iomem *base;
> + int irq;
> + struct clk *clk;
> + const char *map_name;
irq and map_name don't need to be stored here, they're only used in
mtk_ir_probe.
> +};
> +
> +static void mtk_w32_mask(struct mtk_ir *ir, u32 val, u32 mask, unsigned int reg)
> +{
> + u32 tmp;
> +
> + tmp = __raw_readl(ir->base + reg);
> + tmp = (tmp & ~mask) | val;
> + __raw_writel(tmp, ir->base + reg);
> +}
> +
> +static void mtk_w32(struct mtk_ir *ir, u32 val, unsigned int reg)
> +{
> + __raw_writel(val, ir->base + reg);
> +}
> +
> +static u32 mtk_r32(struct mtk_ir *ir, unsigned int reg)
> +{
> + return __raw_readl(ir->base + reg);
> +}
> +
> +static inline void mtk_irq_disable(struct mtk_ir *ir, u32 mask)
> +{
> + u32 val;
> +
> + val = mtk_r32(ir, MTK_IRINT_EN_REG);
> + mtk_w32(ir, val & ~mask, MTK_IRINT_EN_REG);
> +}
> +
> +static inline void mtk_irq_enable(struct mtk_ir *ir, u32 mask)
> +{
> + u32 val;
> +
> + val = mtk_r32(ir, MTK_IRINT_EN_REG);
> + mtk_w32(ir, val | mask, MTK_IRINT_EN_REG);
> +}
> +
> +static irqreturn_t mtk_ir_irq(int irqno, void *dev_id)
> +{
> + struct mtk_ir *ir = dev_id;
> + u8 wid = 0;
> + u32 i, j, val;
> + DEFINE_IR_RAW_EVENT(rawir);
> +
> + spin_lock(&ir->ir_lock);
> +
> + mtk_irq_disable(ir, MTK_IRINT_EN);
> +
> + /* Reset decoder state machine */
> + ir_raw_event_reset(ir->rc);
> +
> + /* First message must be pulse */
> + rawir.pulse = false;
> +
> + /* Handle pulse and space until end of message */
> + for (i = 0 ; i < MTK_CHKDATA_SZ ; i++) {
> + val = mtk_r32(ir, MTK_CHKDATA_REG(i));
> + dev_dbg(ir->dev, "@reg%d=0x%08x\n", i, val);
> +
> + for (j = 0 ; j < 4 ; j++) {
> + wid = (val & (0xff << j * 8)) >> j * 8;
> + rawir.pulse = !rawir.pulse;
> + rawir.duration = wid * (MTK_IR_SAMPLE + 1);
> + ir_raw_event_store_with_filter(ir->rc, &rawir);
> +
> + if (MTK_IR_END(wid))
> + goto end_msg;
> + }
> + }
If I read this correctly, there is a maximum of 17 * 4 = 68 edges per
IR message. The rc6 mce key 0 (scancode 0x800f0400) is 69 edges, so that
won't work.
> +end_msg:
> + /* Restart the next receive */
> + mtk_w32_mask(ir, 0x1, MTK_IRCLR, MTK_IRCLR_REG);
> +
> + ir_raw_event_set_idle(ir->rc, true);
> + ir_raw_event_handle(ir->rc);
> +
> + /* Clear interrupt status */
> + mtk_w32_mask(ir, 0x1, MTK_IRINT_CLR, MTK_IRINT_CLR_REG);
> +
> + /* Enable interrupt */
> + mtk_irq_enable(ir, MTK_IRINT_EN);
> +
> + spin_unlock(&ir->ir_lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int mtk_ir_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *dn = dev->of_node;
> + struct resource *res;
> + struct mtk_ir *ir;
> + u32 val;
> + int ret = 0;
> +
> + ir = devm_kzalloc(dev, sizeof(struct mtk_ir), GFP_KERNEL);
> + if (!ir)
> + return -ENOMEM;
> +
> + spin_lock_init(&ir->ir_lock);
> +
> + ir->dev = dev;
> +
> + if (!of_device_is_compatible(dn, "mediatek,mt7623-ir"))
> + return -ENODEV;
> +
> + ir->clk = devm_clk_get(dev, "clk");
> + if (IS_ERR(ir->clk)) {
> + dev_err(dev, "failed to get a ir clock.\n");
> + return PTR_ERR(ir->clk);
> + }
> +
> + if (clk_prepare_enable(ir->clk)) {
> + dev_err(dev, "try to enable ir_clk failed\n");
> + ret = -EINVAL;
> + goto exit_clkdisable_clk;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ir->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ir->base)) {
> + dev_err(dev, "failed to map registers\n");
> + ret = PTR_ERR(ir->base);
> + goto exit_clkdisable_clk;
> + }
> +
> + ir->rc = rc_allocate_device();
> + if (!ir->rc) {
> + dev_err(dev, "failed to allocate device\n");
> + ret = -ENOMEM;
> + goto exit_clkdisable_clk;
> + }
> +
> + ir->rc->priv = ir;
> + ir->rc->input_name = MTK_IR_DEV;
> + ir->rc->input_phys = "mtk-ir/input0";
> + ir->rc->input_id.bustype = BUS_HOST;
> + ir->rc->input_id.vendor = 0x0001;
> + ir->rc->input_id.product = 0x0001;
> + ir->rc->input_id.version = 0x0001;
> + ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL);
> + ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY;
> + ir->rc->dev.parent = dev;
> + ir->rc->driver_type = RC_DRIVER_IR_RAW;
> + ir->rc->driver_name = MTK_IR_DEV;
> + ir->rc->allowed_protocols = RC_BIT_ALL;
> + ir->rc->rx_resolution = MTK_IR_SAMPLE;
> +
> + ret = rc_register_device(ir->rc);
> + if (ret) {
> + dev_err(dev, "failed to register rc device\n");
> + goto exit_free_dev;
> + }
> +
> + platform_set_drvdata(pdev, ir);
> +
> + ir->irq = platform_get_irq(pdev, 0);
> + if (ir->irq < 0) {
> + dev_err(dev, "no irq resource\n");
> + ret = ir->irq;
>From here on onwards the errors paths should call rc_unregister_device(),
and no longer call rc_free_device(). Note that current master has
devm_rc_allocate_device() and devm_rc_register_device() which would
simplify this code.
> + goto exit_free_dev;
> + }
> +
> + ret = devm_request_irq(dev, ir->irq, mtk_ir_irq, 0, MTK_IR_DEV, ir);
> + if (ret) {
> + dev_err(dev, "failed request irq\n");
> + goto exit_free_dev;
> + }
> +
> + mtk_irq_disable(ir, MTK_IRINT_EN);
> +
> + val = mtk_r32(ir, MTK_CONFIG_HIGH_REG);
> + val |= MTK_PWM_EN | MTK_IR_EN;
> + mtk_w32(ir, val, MTK_CONFIG_HIGH_REG);
> +
> + /* Setting sample period */
> + mtk_w32_mask(ir, MTK_CHK_PERIOD, MTK_CHK_PERIOD_MASK,
> + MTK_CONFIG_LOW_REG);
> +
> + mtk_irq_enable(ir, MTK_IRINT_EN);
> +
> + dev_info(dev, "initialized MT7623 IR driver\n");
> + return 0;
> +
> +exit_free_dev:
> + rc_free_device(ir->rc);
> +exit_clkdisable_clk:
> + clk_disable_unprepare(ir->clk);
> +
> + return ret;
> +}
> +
> +static int mtk_ir_remove(struct platform_device *pdev)
> +{
> + unsigned long flags;
> +
> + struct mtk_ir *ir = platform_get_drvdata(pdev);
> +
> + spin_lock_irqsave(&ir->ir_lock, flags);
> +
> + mtk_irq_disable(ir, MTK_IRINT_EN);
> +
> + clk_disable_unprepare(ir->clk);
> +
> + spin_unlock_irqrestore(&ir->ir_lock, flags);
I'm not convinced the ir_lock is helping to prevent any race condition. An
irq might still already have occurred which will now try to use ir->rc
which is freed. You can remove the spinlock completely and call
sychronize_irq() after disabling the mtk interrupt. That way you're sure
the remove is safe to complete.
> +
> + rc_unregister_device(ir->rc);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id mtk_ir_match[] = {
> + { .compatible = "mediatek,mt7623-ir" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, mtk_ir_match);
> +
> +static struct platform_driver mtk_ir_driver = {
> + .probe = mtk_ir_probe,
> + .remove = mtk_ir_remove,
> + .driver = {
> + .name = MTK_IR_DEV,
> + .of_match_table = mtk_ir_match,
> + },
> +};
> +
> +module_platform_driver(mtk_ir_driver);
> +
> +MODULE_DESCRIPTION("Mediatek MT7623 IR Receiver Controller Driver");
> +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
> +MODULE_LICENSE("GPL");
> --
> 1.9.1
>
^ permalink raw reply
* [PATCH] ARM: dts: sunxi: Use axp209.dtsi for Olinuxino Lime2
From: Maxime Ripard @ 2017-01-05 17:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161227102238.20110-1-manu@bidouilliste.com>
On Tue, Dec 27, 2016 at 11:22:38AM +0100, Emmanuel Vadot wrote:
> Use axp209.dtsi in sun7i-a20-olinuxino-lime2.dts and correct
> some regulators.
>
> DCDC2 is used for vdd-cpu so it should never be bellow 1V and above 1.4V
> DCDC3 is used for VDD_INT so same as above.
> LD01 is used for the RTC, and should have a typical value of 1.3V
> LD02 is used for AVCC and should have a typical value of 3.0V
> LD03/4 are used for Port-E/Port-G Power pin, and the schematics recommands
> to set them to 2.8V as they can be used for CSI0/1.
>
> Signed-off-by: Emmanuel Vadot <manu@bidouilliste.com>
Applied, thanks!
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/3536d00f/attachment.sig>
^ permalink raw reply
* [PATCH v3] ARM: dts: sunxi: Add num-cs for A20 spi nodes
From: Maxime Ripard @ 2017-01-05 17:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161227102807.20244-1-manu@bidouilliste.com>
On Tue, Dec 27, 2016 at 11:28:07AM +0100, Emmanuel Vadot wrote:
> The spi0 controller on the A20 have up to 4 CS (Chip Select) while the
> others three only have 1.
> Add the num-cs property to each node.
> The current driver doesn't read this property but this is useful for
> downstream user of DTS (FreeBSD for example).
>
> Signed-off-by: Emmanuel Vadot <manu@bidouilliste.com>
Applied, thanks!
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/1f29aa7b/attachment.sig>
^ permalink raw reply
* [PATCH 15/20] ARM/hw_breakpoint: Convert to hotplug state machine
From: Mark Rutland @ 2017-01-05 17:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CACRpkdYCG71AUe9iBdV+2t_LNFSzYO18vM=-pa=jWDTT53FnwA@mail.gmail.com>
On Thu, Jan 05, 2017 at 04:26:45PM +0100, Linus Walleij wrote:
> On Wed, Jan 4, 2017 at 2:56 PM, Mark Rutland <mark.rutland@arm.com> wrote:
> > Linus, I wasn't able to get ethernet working. Do I need anything on top
> > of v4.10-rc2 && multi_v7_defconfig?
>
> I haven't tried it with multi_v7 but I should probably try that and patch
> up the defconfigs, those are probably the root of the problem.
>
> I do this on top of qcom_defconfig:
>
> scripts/config --file .config \
> --enable QCOM_EBI2 \
> --enable ETHERNET \
> --enable NET_VENDOR_SMSC \
> --enable SMSC911X
>
> Maybe you are missing the EBI2 config?
That was it, yes! With QCOM_EBI2 atop of multi_v7_defconfig (along with
a hack to the hw_breakpoint code), I can boot to an NFS filesystem.
Thanks,
Mark.
^ permalink raw reply
* [PATCH] ARM: dts: sunxi: Enable spi1 and spi2 for Olimex A20 SOM EVB
From: Maxime Ripard @ 2017-01-05 17:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161226175349.17712-1-manu@bidouilliste.com>
Hi,
On Mon, Dec 26, 2016 at 06:53:49PM +0100, Emmanuel Vadot wrote:
> Enable the spi1 and spi2 node since the pins are exposed on the UEXT
> connectors.
>
> Signed-off-by: Emmanuel Vadot <manu@bidouilliste.com>
> ---
> arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts
> index 669a1c338c76..fa8c6f60552b 100644
> --- a/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts
> +++ b/arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts
> @@ -300,12 +300,14 @@
> pinctrl-names = "default";
> pinctrl-0 = <&spi1_pins_a>,
> <&spi1_cs0_pins_a>;
> + status = "okay";
> };
>
> &spi2 {
> pinctrl-names = "default";
> pinctrl-0 = <&spi2_pins_a>,
> <&spi2_cs0_pins_a>;
> + status = "okay";
> };
Those nodes don't exist unfortunately. Maybe you forgot to send one
patch?
Thanks!
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/d6854ac6/attachment.sig>
^ permalink raw reply
* [PATCHv2 1/3] pci: pci-aardvark: move to MSI handling using generic MSI support
From: Marc Zyngier @ 2017-01-05 17:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1482484854-26418-2-git-send-email-thomas.petazzoni@free-electrons.com>
Hi Thomas,
On 23/12/16 09:20, Thomas Petazzoni wrote:
> The MSI support introduced with the initial Aardvark driver was based
> on the msi_controller structure and the of_pci_msi_chip_add() /
> of_pci_find_msi_chip_by_node() API, which are being deprecated in
> favor of the generic MSI support.
>
> Therefore, this commit updates the Aardvark driver to use the generic
> MSI support.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
> Changes since v1:
> - Rebased on top of v4.9
> - Removed the MSI_FLAG_PCI_MSIX flag since we don't support MSI-X for
> the moment.
> ---
> drivers/pci/host/pci-aardvark.c | 172 +++++++++++++++-------------------------
> 1 file changed, 66 insertions(+), 106 deletions(-)
>
> diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
> index 4fce494..3fdca5d 100644
> --- a/drivers/pci/host/pci-aardvark.c
> +++ b/drivers/pci/host/pci-aardvark.c
> @@ -200,10 +200,12 @@ struct advk_pcie {
> struct list_head resources;
> struct irq_domain *irq_domain;
> struct irq_chip irq_chip;
> - struct msi_controller msi;
> struct irq_domain *msi_domain;
> + struct irq_domain *msi_inner_domain;
> + struct irq_chip msi_bottom_irq_chip;
> struct irq_chip msi_irq_chip;
> - DECLARE_BITMAP(msi_irq_in_use, MSI_IRQ_NUM);
> + struct msi_domain_info msi_domain_info;
> + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
> struct mutex msi_used_lock;
> u16 msi_msg;
> int root_bus_nr;
> @@ -545,94 +547,64 @@ static struct pci_ops advk_pcie_ops = {
> .write = advk_pcie_wr_conf,
> };
>
> -static int advk_pcie_alloc_msi(struct advk_pcie *pcie)
> +static void advk_msi_irq_compose_msi_msg(struct irq_data *data,
> + struct msi_msg *msg)
> {
> - int hwirq;
> + struct advk_pcie *pcie = irq_data_get_irq_chip_data(data);
> + phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg);
>
> - mutex_lock(&pcie->msi_used_lock);
> - hwirq = find_first_zero_bit(pcie->msi_irq_in_use, MSI_IRQ_NUM);
> - if (hwirq >= MSI_IRQ_NUM)
> - hwirq = -ENOSPC;
> - else
> - set_bit(hwirq, pcie->msi_irq_in_use);
> - mutex_unlock(&pcie->msi_used_lock);
> -
> - return hwirq;
> + msg->address_lo = lower_32_bits(msi_msg);
> + msg->address_hi = upper_32_bits(msi_msg);
> + msg->data = data->irq;
> }
>
> -static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq)
> +static int advk_msi_set_affinity(struct irq_data *irq_data,
> + const struct cpumask *mask, bool force)
> {
> - struct device *dev = &pcie->pdev->dev;
> -
> - mutex_lock(&pcie->msi_used_lock);
> - if (!test_bit(hwirq, pcie->msi_irq_in_use))
> - dev_err(dev, "trying to free unused MSI#%d\n", hwirq);
> - else
> - clear_bit(hwirq, pcie->msi_irq_in_use);
> - mutex_unlock(&pcie->msi_used_lock);
> + return -EINVAL;
> }
>
> -static int advk_pcie_setup_msi_irq(struct msi_controller *chip,
> - struct pci_dev *pdev,
> - struct msi_desc *desc)
> +static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
> + unsigned int virq,
> + unsigned int nr_irqs, void *args)
> {
> - struct advk_pcie *pcie = pdev->bus->sysdata;
> - struct msi_msg msg;
> - int virq, hwirq;
> - phys_addr_t msi_msg_phys;
> -
> - /* We support MSI, but not MSI-X */
> - if (desc->msi_attrib.is_msix)
> - return -EINVAL;
> -
> - hwirq = advk_pcie_alloc_msi(pcie);
> - if (hwirq < 0)
> - return hwirq;
> + struct advk_pcie *pcie = domain->host_data;
> + int hwirq, i;
>
> - virq = irq_create_mapping(pcie->msi_domain, hwirq);
> - if (!virq) {
> - advk_pcie_free_msi(pcie, hwirq);
> - return -EINVAL;
> + mutex_lock(&pcie->msi_used_lock);
> + hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM,
> + 0, nr_irqs, 0);
> + if (hwirq >= MSI_IRQ_NUM) {
> + mutex_unlock(&pcie->msi_used_lock);
> + return -ENOSPC;
> }
>
> - irq_set_msi_desc(virq, desc);
> -
> - msi_msg_phys = virt_to_phys(&pcie->msi_msg);
> -
> - msg.address_lo = lower_32_bits(msi_msg_phys);
> - msg.address_hi = upper_32_bits(msi_msg_phys);
> - msg.data = virq;
> -
> - pci_write_msi_msg(virq, &msg);
> -
> - return 0;
> -}
> + bitmap_set(pcie->msi_used, hwirq, nr_irqs);
> + mutex_unlock(&pcie->msi_used_lock);
>
> -static void advk_pcie_teardown_msi_irq(struct msi_controller *chip,
> - unsigned int irq)
> -{
> - struct irq_data *d = irq_get_irq_data(irq);
> - struct msi_desc *msi = irq_data_get_msi_desc(d);
> - struct advk_pcie *pcie = msi_desc_to_pci_sysdata(msi);
> - unsigned long hwirq = d->hwirq;
> + for (i = 0; i < nr_irqs; i++)
> + irq_domain_set_info(domain, virq + i, hwirq + i,
> + &pcie->msi_bottom_irq_chip,
> + domain->host_data, handle_simple_irq,
> + NULL, NULL);
Given that you do not support multi-MSI, this function should never be
called with nr_irqs != 1. Which means you could loose that loop.
>
> - irq_dispose_mapping(irq);
> - advk_pcie_free_msi(pcie, hwirq);
> + return hwirq;
> }
>
> -static int advk_pcie_msi_map(struct irq_domain *domain,
> - unsigned int virq, irq_hw_number_t hw)
> +static void advk_msi_irq_domain_free(struct irq_domain *domain,
> + unsigned int virq, unsigned int nr_irqs)
> {
> + struct irq_data *d = irq_domain_get_irq_data(domain, virq);
> struct advk_pcie *pcie = domain->host_data;
>
> - irq_set_chip_and_handler(virq, &pcie->msi_irq_chip,
> - handle_simple_irq);
> -
> - return 0;
> + mutex_lock(&pcie->msi_used_lock);
> + bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs);
> + mutex_unlock(&pcie->msi_used_lock);
> }
>
> -static const struct irq_domain_ops advk_pcie_msi_irq_ops = {
> - .map = advk_pcie_msi_map,
> +static const struct irq_domain_ops advk_msi_domain_ops = {
> + .alloc = advk_msi_irq_domain_alloc,
> + .free = advk_msi_irq_domain_free,
> };
>
> static void advk_pcie_irq_mask(struct irq_data *d)
> @@ -680,30 +652,24 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
> {
> struct device *dev = &pcie->pdev->dev;
> struct device_node *node = dev->of_node;
> - struct irq_chip *msi_irq_chip;
> - struct msi_controller *msi;
> + struct irq_chip *bottom_ic, *msi_ic;
> + struct msi_domain_info *msi_di;
> phys_addr_t msi_msg_phys;
> - int ret;
>
> - msi_irq_chip = &pcie->msi_irq_chip;
> + mutex_init(&pcie->msi_used_lock);
>
> - msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi",
> - dev_name(dev));
> - if (!msi_irq_chip->name)
> - return -ENOMEM;
> + bottom_ic = &pcie->msi_bottom_irq_chip;
>
> - msi_irq_chip->irq_enable = pci_msi_unmask_irq;
> - msi_irq_chip->irq_disable = pci_msi_mask_irq;
> - msi_irq_chip->irq_mask = pci_msi_mask_irq;
> - msi_irq_chip->irq_unmask = pci_msi_unmask_irq;
> + bottom_ic->name = "MSI";
> + bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg;
> + bottom_ic->irq_set_affinity = advk_msi_set_affinity;
>
> - msi = &pcie->msi;
> + msi_ic = &pcie->msi_irq_chip;
> + msi_ic->name = "advk-MSI";
>
> - msi->setup_irq = advk_pcie_setup_msi_irq;
> - msi->teardown_irq = advk_pcie_teardown_msi_irq;
> - msi->of_node = node;
> -
> - mutex_init(&pcie->msi_used_lock);
> + msi_di = &pcie->msi_domain_info;
> + msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS;
> + msi_di->chip = msi_ic;
>
> msi_msg_phys = virt_to_phys(&pcie->msi_msg);
>
> @@ -712,16 +678,18 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
> advk_writel(pcie, upper_32_bits(msi_msg_phys),
> PCIE_MSI_ADDR_HIGH_REG);
>
> - pcie->msi_domain =
> + pcie->msi_inner_domain =
> irq_domain_add_linear(NULL, MSI_IRQ_NUM,
> - &advk_pcie_msi_irq_ops, pcie);
> - if (!pcie->msi_domain)
> + &advk_msi_domain_ops, pcie);
> + if (!pcie->msi_inner_domain)
> return -ENOMEM;
>
> - ret = of_pci_msi_chip_add(msi);
> - if (ret < 0) {
> - irq_domain_remove(pcie->msi_domain);
> - return ret;
> + pcie->msi_domain =
> + pci_msi_create_irq_domain(of_node_to_fwnode(node),
> + msi_di, pcie->msi_inner_domain);
> + if (!pcie->msi_domain) {
> + irq_domain_remove(pcie->msi_inner_domain);
> + return -ENOMEM;
> }
>
> return 0;
> @@ -729,8 +697,8 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
>
> static void advk_pcie_remove_msi_irq_domain(struct advk_pcie *pcie)
> {
> - of_pci_msi_chip_remove(&pcie->msi);
> irq_domain_remove(pcie->msi_domain);
> + irq_domain_remove(pcie->msi_inner_domain);
> }
>
> static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
> @@ -917,8 +885,6 @@ static int advk_pcie_probe(struct platform_device *pdev)
> struct advk_pcie *pcie;
> struct resource *res;
> struct pci_bus *bus, *child;
> - struct msi_controller *msi;
> - struct device_node *msi_node;
> int ret, irq;
>
> pcie = devm_kzalloc(dev, sizeof(struct advk_pcie), GFP_KERNEL);
> @@ -962,14 +928,8 @@ static int advk_pcie_probe(struct platform_device *pdev)
> return ret;
> }
>
> - msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
> - if (msi_node)
> - msi = of_pci_find_msi_chip_by_node(msi_node);
> - else
> - msi = NULL;
> -
> - bus = pci_scan_root_bus_msi(dev, 0, &advk_pcie_ops,
> - pcie, &pcie->resources, &pcie->msi);
> + bus = pci_scan_root_bus(dev, 0, &advk_pcie_ops,
> + pcie, &pcie->resources);
> if (!bus) {
> advk_pcie_remove_msi_irq_domain(pcie);
> advk_pcie_remove_irq_domain(pcie);
>
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH v3 3/9] arm64: cpufeature: Cleanup feature bit tables
From: Catalin Marinas @ 2017-01-05 17:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483552147-9605-4-git-send-email-suzuki.poulose@arm.com>
On Wed, Jan 04, 2017 at 05:49:01PM +0000, Suzuki K. Poulose wrote:
> This patch does the following clean ups :
>
> 1) All undescribed fields of a register are now treated as "strict"
> with a safe value of 0. Hence we could leave an empty table for
> describing registers which are RAZ.
>
> 2) ID_AA64DFR1_EL1 is RAZ and should use the table for RAZ register.
>
> 3) ftr_generic32 is used to represent a register with a 32bit feature
> value. Rename this to ftr_singl32 to make it more obvious. Since
> we don't have a 64bit singe feature register, kill ftr_generic.
Nitpick: couple of "single" typos above.
>
> Based on a patch by Mark Rutland.
>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* [PATCH 1/6] ARM: dts: am335x-phycore-som: Update NAND partition table
From: Tony Lindgren @ 2017-01-05 17:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105153637.GA4310@atomide.com>
* Tony Lindgren <tony@atomide.com> [170105 07:37]:
> * Teresa Remmet <t.remmet@phytec.de> [170105 06:57]:
> > To improve NAND safety we updated the partition layout.
> > Added barebox backup partition and removed kernel and oftree
> > partition. They are kept in ubi now.
>
> What about the users with earlier partition tables?
>
> Please read about "flag day" changes, typically they are not
> acceptable.
Adding Brian and Adam to Cc. Can you guys come up with some
solution on this?
I'm suggesting we leave the kernel nodes empty and let u-boot
populate them, so maybe you guys can discuss this on the related
lists.
The rest of the series looks fine to me so applying it into
omap-for-v4.11/dt.
Cheers,
Tony
^ permalink raw reply
* [PATCH 1/2] ARM: dts: sun7i: Add wifi dt node on Banana Pro
From: Maxime Ripard @ 2017-01-05 17:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170104142250.22171-1-joerg.krause@embedded.rocks>
Hi,
On Wed, Jan 04, 2017 at 03:22:49PM +0100, J?rg Krause wrote:
> The Banana Pro has an AMPAK AP6181 WiFi+Bluetooth module. The WiFi part
> is a BCM43362 IC connected to MMC3 of the A20 SoC via SDIO. The IC also
> takes a power enable signal via GPIO.
>
> This commit adds a device-tree node to power it up, so the mmc subsys
> can scan it, and enables the mmc controller which is connected to it.
>
> As the wifi enable pin of the AP6181 module is not really a regulator,
> switch the mmc3 node to the mmc-pwrseq framework for controlling it.
> This more accurately reflectes how the hardware actually works.
>
> Signed-off-by: J?rg Krause <joerg.krause@embedded.rocks>
> ---
> arch/arm/boot/dts/sun7i-a20-bananapro.dts | 35 ++++++++++++++++++++-----------
> 1 file changed, 23 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/boot/dts/sun7i-a20-bananapro.dts b/arch/arm/boot/dts/sun7i-a20-bananapro.dts
> index 19d63d4049de..439ad50dcd4a 100644
> --- a/arch/arm/boot/dts/sun7i-a20-bananapro.dts
> +++ b/arch/arm/boot/dts/sun7i-a20-bananapro.dts
> @@ -76,6 +76,13 @@
> };
> };
>
> + mmc3_pwrseq: mmc3_pwrseq {
I'd rather have it named by its function rather than what it's
connected to (ie called wifi_pwrseq, or something like that). The node
names should also use dashes and not underscores.
> + compatible = "mmc-pwrseq-simple";
> + pinctrl-names = "default";
> + pinctrl-0 = <&vmmc3_pin_bananapro>;
> + reset-gpios = <&pio 7 22 GPIO_ACTIVE_LOW>;
> + };
> +
> reg_gmac_3v3: gmac-3v3 {
> compatible = "regulator-fixed";
> pinctrl-names = "default";
> @@ -87,17 +94,6 @@
> enable-active-high;
> gpio = <&pio 7 23 GPIO_ACTIVE_HIGH>;
> };
> -
> - reg_vmmc3: vmmc3 {
> - compatible = "regulator-fixed";
> - pinctrl-names = "default";
> - pinctrl-0 = <&vmmc3_pin_bananapro>;
> - regulator-name = "vmmc3";
> - regulator-min-microvolt = <3300000>;
> - regulator-max-microvolt = <3300000>;
> - enable-active-high;
> - gpio = <&pio 7 22 GPIO_ACTIVE_HIGH>;
> - };
> };
>
> &ahci {
> @@ -166,10 +162,25 @@
> &mmc3 {
> pinctrl-names = "default";
> pinctrl-0 = <&mmc3_pins_a>;
> - vmmc-supply = <®_vmmc3>;
> + vmmc-supply = <®_vcc3v3>;
> + mmc-pwrseq = <&mmc3_pwrseq>;
> bus-width = <4>;
> non-removable;
> + wakeup-source;
> status = "okay";
> +
> + brcmf: bcrmf at 1 {
> + reg = <1>;
> + compatible = "brcm,bcm4329-fmac";
> + interrupt-parent = <&pio>;
> + interrupts = <7 15 IRQ_TYPE_LEVEL_LOW>;
> + interrupt-names = "host-wake";
> + };
> +};
> +
> +&mmc3_pins_a {
> + /* AP6181 requires pull-up */
> + allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
> };
This is the default now.
Thanks!
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/ba6de88c/attachment.sig>
^ permalink raw reply
* [PATCH 2/2] ARM: dts: sun7i: Enable audio codec on Banana Pro
From: Maxime Ripard @ 2017-01-05 17:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170104142250.22171-2-joerg.krause@embedded.rocks>
On Wed, Jan 04, 2017 at 03:22:50PM +0100, J?rg Krause wrote:
> This commit enables the on-chip audio codec present on the A20 SoC
> on the Banana Pro board.
>
> Signed-off-by: J?rg Krause <joerg.krause@embedded.rocks>
Applied, thanks!
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: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/908d80a0/attachment.sig>
^ permalink raw reply
* [PATCH v3 5/9] arm64: cpufeature: Define helpers for sys_reg id
From: Catalin Marinas @ 2017-01-05 17:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483552147-9605-6-git-send-email-suzuki.poulose@arm.com>
On Wed, Jan 04, 2017 at 05:49:03PM +0000, Suzuki K. Poulose wrote:
> Define helper macros to extract op0, op1, CRn, CRm & op2
> for a given sys_reg id. While at it remove the explicit
> masking only used for Op0.
>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* [PATCH v3 6/9] arm64: Add helper to decode register from instruction
From: Catalin Marinas @ 2017-01-05 17:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483552147-9605-7-git-send-email-suzuki.poulose@arm.com>
On Wed, Jan 04, 2017 at 05:49:04PM +0000, Suzuki K. Poulose wrote:
> Add a helper to extract the register field from a given
> instruction.
>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
^ permalink raw reply
* [PATCH] ARM: dts: am335x-icev2: Remove the duplicated pinmux setting
From: Tony Lindgren @ 2017-01-05 17:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170104050940.25698-1-lokeshvutla@ti.com>
* Lokesh Vutla <lokeshvutla@ti.com> [170103 21:12]:
> There is no mmc sd card detect on am335x-ice board. But the spi0_cs1
> pin being configured as mmcsd_cd. Removing it fixes the below warning
> during boot.
Applying into omap-for-v4.11/fixes thanks.
Tony
^ permalink raw reply
* [PATCH] ARM: OMAP1: DMA: Correct the number of logical channels
From: Tony Lindgren @ 2017-01-05 17:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103112234.19097-1-peter.ujfalusi@ti.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com> [170103 03:22]:
> OMAP1510, OMAP5910 and OMAP310 have only 9 logical channels.
> OMAP1610, OMAP5912, OMAP1710, OMAP730, and OMAP850 have 16 logical channels
> available.
>
> The wired 17 for the lch_count must have been used to cover the 16 + 1
> dedicated LCD channel, in reality we can only use 9 or 16 channels.
>
> The d->chan_count is not used by the omap-dma stack, so we can skip the
> setup. chan_count was configured to the number of logical channels and not
> the actual number of physical channels anyways.
Aaro care to ack?
Tony
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
> arch/arm/mach-omap1/dma.c | 16 +++++++---------
> 1 file changed, 7 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm/mach-omap1/dma.c b/arch/arm/mach-omap1/dma.c
> index f6ba589cd312..c821c1d5610e 100644
> --- a/arch/arm/mach-omap1/dma.c
> +++ b/arch/arm/mach-omap1/dma.c
> @@ -32,7 +32,6 @@
> #include "soc.h"
>
> #define OMAP1_DMA_BASE (0xfffed800)
> -#define OMAP1_LOGICAL_DMA_CH_COUNT 17
>
> static u32 enable_1510_mode;
>
> @@ -348,8 +347,6 @@ static int __init omap1_system_dma_init(void)
> goto exit_iounmap;
> }
>
> - d->lch_count = OMAP1_LOGICAL_DMA_CH_COUNT;
> -
> /* Valid attributes for omap1 plus processors */
> if (cpu_is_omap15xx())
> d->dev_caps = ENABLE_1510_MODE;
> @@ -366,13 +363,14 @@ static int __init omap1_system_dma_init(void)
> d->dev_caps |= CLEAR_CSR_ON_READ;
> d->dev_caps |= IS_WORD_16;
>
> - if (cpu_is_omap15xx())
> - d->chan_count = 9;
> - else if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
> - if (!(d->dev_caps & ENABLE_1510_MODE))
> - d->chan_count = 16;
> + /* available logical channels */
> + if (cpu_is_omap15xx()) {
> + d->lch_count = 9;
> + } else {
> + if (d->dev_caps & ENABLE_1510_MODE)
> + d->lch_count = 9;
> else
> - d->chan_count = 9;
> + d->lch_count = 16;
> }
>
> p = dma_plat_info;
> --
> 2.11.0
>
^ permalink raw reply
* [PATCH] ARM: hw_breakpoint: blacklist Scorpion CPUs
From: Mark Rutland @ 2017-01-05 17:32 UTC (permalink / raw)
To: linux-arm-kernel
On APQ8060, the kernel crashes in arch_hw_breakpoint_init, taking an
undefined instruction trap within write_wb_reg. This is because Scorpion
CPUs erroneously appear to set DBGPRSR.SPD when WFI is issued, even if
the core is not powered down. When DBGPRSR.SPD is set, breakpoint and
watchpoint registers are treated as undefined.
It's possible to trigger similar crashes later on from userspace, by
requesting the kernel to install a breakpoint or watchpoint, as we can
go idle at any point between the reset of the debug registers and their
later use. This has always been the case.
Given that this has always been broken, no-one has complained until now,
and there is no clear workaround, disable hardware breakpoints and
watchpoints on Scorpion to avoid these issues.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reported-by: Linus Walleij <linus.walleij@linaro.org>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: stable at vger.kernel.org
---
arch/arm/include/asm/cputype.h | 3 +++
arch/arm/kernel/hw_breakpoint.c | 16 ++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
index 522b5fe..b62eaeb 100644
--- a/arch/arm/include/asm/cputype.h
+++ b/arch/arm/include/asm/cputype.h
@@ -94,6 +94,9 @@
#define ARM_CPU_XSCALE_ARCH_V2 0x4000
#define ARM_CPU_XSCALE_ARCH_V3 0x6000
+/* Qualcomm implemented cores */
+#define ARM_CPU_PART_SCORPION 0x510002d0
+
extern unsigned int processor_id;
#ifdef CONFIG_CPU_CP15
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 188180b..5d68ff9 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -1063,6 +1063,22 @@ static int __init arch_hw_breakpoint_init(void)
return 0;
}
+ /*
+ * Scorpion CPUs (at least those in APQ8060) seem to set DBGPRSR.SPD
+ * whenever a WFI is issued, even if the core is not powered down, in
+ * violation of the architecture. When DBGPRSR.SPD is set, accesses to
+ * breakpoint and watchpoint registers are treated as undefined, so
+ * this results in boot time and runtime failures when these are
+ * accessed and we unexpectedly take a trap.
+ *
+ * It's not clear if/how this can be worked around, so we blacklist
+ * Scorpion CPUs to avoid these issues.
+ */
+ if (read_cpuid_part() == ARM_CPU_PART_SCORPION) {
+ pr_info("Scorpion CPU detected. Breakpoints and watchpoints disabled\n");
+ return 0;
+ }
+
has_ossr = core_has_os_save_restore();
/* Determine how many BRPs/WRPs are available. */
--
1.9.1
^ permalink raw reply related
* [PATCHv3 3/8] rtc: add STM32 RTC driver
From: Mathieu Poirier @ 2017-01-05 17:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483623809-29937-4-git-send-email-amelie.delaunay@st.com>
On Thu, Jan 05, 2017 at 02:43:24PM +0100, Amelie Delaunay wrote:
> This patch adds support for the STM32 RTC.
>
> Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
> ---
> drivers/rtc/Kconfig | 11 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-stm32.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 788 insertions(+)
> create mode 100644 drivers/rtc/rtc-stm32.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index e859d14..11eb28a 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1706,6 +1706,17 @@ config RTC_DRV_PIC32
> This driver can also be built as a module. If so, the module
> will be called rtc-pic32
>
> +config RTC_DRV_STM32
> + tristate "STM32 RTC"
> + select REGMAP_MMIO
> + depends on ARCH_STM32 || COMPILE_TEST
> + help
> + If you say yes here you get support for the STM32 On-Chip
> + Real Time Clock.
> +
> + This driver can also be built as a module, if so, the module
> + will be called "rtc-stm32".
> +
> comment "HID Sensor RTC drivers"
>
> config RTC_DRV_HID_SENSOR_TIME
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 1ac694a..87bd9cc 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -144,6 +144,7 @@ obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
> obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
> obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
> obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
> +obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
> obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
> obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
> obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
> diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
> new file mode 100644
> index 0000000..fdd3a31
> --- /dev/null
> +++ b/drivers/rtc/rtc-stm32.c
> @@ -0,0 +1,776 @@
> +/*
> + * Copyright (C) Amelie Delaunay 2016
> + * Author: Amelie Delaunay <amelie.delaunay@st.com>
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/bcd.h>
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/ioport.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/rtc.h>
> +#include <linux/spinlock.h>
> +
> +#define DRIVER_NAME "stm32_rtc"
> +
> +/* STM32 RTC registers */
> +#define STM32_RTC_TR 0x00
> +#define STM32_RTC_DR 0x04
> +#define STM32_RTC_CR 0x08
> +#define STM32_RTC_ISR 0x0C
> +#define STM32_RTC_PRER 0x10
> +#define STM32_RTC_ALRMAR 0x1C
> +#define STM32_RTC_WPR 0x24
> +
> +/* STM32_RTC_TR bit fields */
> +#define STM32_RTC_TR_SEC_SHIFT 0
> +#define STM32_RTC_TR_SEC GENMASK(6, 0)
> +#define STM32_RTC_TR_MIN_SHIFT 8
> +#define STM32_RTC_TR_MIN GENMASK(14, 8)
> +#define STM32_RTC_TR_HOUR_SHIFT 16
> +#define STM32_RTC_TR_HOUR GENMASK(21, 16)
> +
> +/* STM32_RTC_DR bit fields */
> +#define STM32_RTC_DR_DATE_SHIFT 0
> +#define STM32_RTC_DR_DATE GENMASK(5, 0)
> +#define STM32_RTC_DR_MONTH_SHIFT 8
> +#define STM32_RTC_DR_MONTH GENMASK(12, 8)
> +#define STM32_RTC_DR_WDAY_SHIFT 13
> +#define STM32_RTC_DR_WDAY GENMASK(15, 13)
> +#define STM32_RTC_DR_YEAR_SHIFT 16
> +#define STM32_RTC_DR_YEAR GENMASK(23, 16)
> +
> +/* STM32_RTC_CR bit fields */
> +#define STM32_RTC_CR_FMT BIT(6)
> +#define STM32_RTC_CR_ALRAE BIT(8)
> +#define STM32_RTC_CR_ALRAIE BIT(12)
> +
> +/* STM32_RTC_ISR bit fields */
> +#define STM32_RTC_ISR_ALRAWF BIT(0)
> +#define STM32_RTC_ISR_INITS BIT(4)
> +#define STM32_RTC_ISR_RSF BIT(5)
> +#define STM32_RTC_ISR_INITF BIT(6)
> +#define STM32_RTC_ISR_INIT BIT(7)
> +#define STM32_RTC_ISR_ALRAF BIT(8)
> +
> +/* STM32_RTC_PRER bit fields */
> +#define STM32_RTC_PRER_PRED_S_SHIFT 0
> +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
> +#define STM32_RTC_PRER_PRED_A_SHIFT 16
> +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
> +
> +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
> +#define STM32_RTC_ALRMXR_SEC_SHIFT 0
> +#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0)
> +#define STM32_RTC_ALRMXR_SEC_MASK BIT(7)
> +#define STM32_RTC_ALRMXR_MIN_SHIFT 8
> +#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8)
> +#define STM32_RTC_ALRMXR_MIN_MASK BIT(15)
> +#define STM32_RTC_ALRMXR_HOUR_SHIFT 16
> +#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16)
> +#define STM32_RTC_ALRMXR_PM BIT(22)
> +#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23)
> +#define STM32_RTC_ALRMXR_DATE_SHIFT 24
> +#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24)
> +#define STM32_RTC_ALRMXR_WDSEL BIT(30)
> +#define STM32_RTC_ALRMXR_WDAY_SHIFT 24
> +#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24)
> +#define STM32_RTC_ALRMXR_DATE_MASK BIT(31)
> +
> +/* STM32_RTC_WPR key constants */
> +#define RTC_WPR_1ST_KEY 0xCA
> +#define RTC_WPR_2ND_KEY 0x53
> +#define RTC_WPR_WRONG_KEY 0xFF
> +
> +/*
> + * RTC registers are protected agains parasitic write access.
> + * PWR_CR_DBP bit must be set to enable write access to RTC registers.
> + */
> +/* STM32_PWR_CR */
> +#define PWR_CR 0x00
> +/* STM32_PWR_CR bit field */
> +#define PWR_CR_DBP BIT(8)
> +
> +static struct regmap *dbp;
> +
> +struct stm32_rtc {
> + struct rtc_device *rtc_dev;
> + void __iomem *base;
> + struct clk *ck_rtc;
> + spinlock_t lock; /* Protects registers accesses */
> + int irq_alarm;
> +};
> +
> +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
> +{
> + writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + STM32_RTC_WPR);
> + writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + STM32_RTC_WPR);
> +}
> +
> +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
> +{
> + writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + STM32_RTC_WPR);
> +}
> +
> +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
> +{
> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + if (!(isr & STM32_RTC_ISR_INITF)) {
> + isr |= STM32_RTC_ISR_INIT;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +
> + /*
> + * It takes around 2 ck_rtc clock cycles to enter in
> + * initialization phase mode (and have INITF flag set). As
> + * slowest ck_rtc frequency may be 32kHz and highest should be
> + * 1MHz, we poll every 10 us with a timeout of 100ms.
> + */
> + return readl_relaxed_poll_timeout_atomic(
> + rtc->base + STM32_RTC_ISR,
> + isr, (isr & STM32_RTC_ISR_INITF),
> + 10, 100000);
> + }
> +
> + return 0;
> +}
> +
> +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
> +{
> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + isr &= ~STM32_RTC_ISR_INIT;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +}
> +
> +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
> +{
> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + isr &= ~STM32_RTC_ISR_RSF;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +
> + /*
> + * Wait for RSF to be set to ensure the calendar registers are
> + * synchronised, it takes around 2 ck_rtc clock cycles
> + */
> + return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
> + isr,
> + (isr & STM32_RTC_ISR_RSF),
> + 10, 100000);
> +}
> +
> +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
> +{
> + struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
> + unsigned int isr, cr;
> +
> + mutex_lock(&rtc->rtc_dev->ops_lock);
> +
> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> +
> + if ((isr & STM32_RTC_ISR_ALRAF) &&
> + (cr & STM32_RTC_CR_ALRAIE)) {
> + /* Alarm A flag - Alarm interrupt */
> + dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n");
> +
> + /* Pass event to the kernel */
> + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
> +
> + /* Clear event flag, otherwise new events won't be received */
> + writel_relaxed(isr & ~STM32_RTC_ISR_ALRAF,
> + rtc->base + STM32_RTC_ISR);
> + }
> +
> + mutex_unlock(&rtc->rtc_dev->ops_lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/* Convert rtc_time structure from bin to bcd format */
> +static void tm2bcd(struct rtc_time *tm)
> +{
> + tm->tm_sec = bin2bcd(tm->tm_sec);
> + tm->tm_min = bin2bcd(tm->tm_min);
> + tm->tm_hour = bin2bcd(tm->tm_hour);
> +
> + tm->tm_mday = bin2bcd(tm->tm_mday);
> + tm->tm_mon = bin2bcd(tm->tm_mon + 1);
> + tm->tm_year = bin2bcd(tm->tm_year - 100);
> + /*
> + * Number of days since Sunday
> + * - on kernel side, 0=Sunday...6=Saturday
> + * - on rtc side, 0=invalid,1=Monday...7=Sunday
> + */
> + tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
> +}
> +
> +/* Convert rtc_time structure from bcd to bin format */
> +static void bcd2tm(struct rtc_time *tm)
> +{
> + tm->tm_sec = bcd2bin(tm->tm_sec);
> + tm->tm_min = bcd2bin(tm->tm_min);
> + tm->tm_hour = bcd2bin(tm->tm_hour);
> +
> + tm->tm_mday = bcd2bin(tm->tm_mday);
> + tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
> + tm->tm_year = bcd2bin(tm->tm_year) + 100;
> + /*
> + * Number of days since Sunday
> + * - on kernel side, 0=Sunday...6=Saturday
> + * - on rtc side, 0=invalid,1=Monday...7=Sunday
> + */
> + tm->tm_wday %= 7;
> +}
> +
> +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + unsigned int tr, dr;
> + unsigned long irqflags;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + /* Time and Date in BCD format */
> + tr = readl_relaxed(rtc->base + STM32_RTC_TR);
> + dr = readl_relaxed(rtc->base + STM32_RTC_DR);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
> + tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
> + tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
> +
> + tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
> + tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
> + tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
> + tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
> +
> + /* We don't report tm_yday and tm_isdst */
> +
> + bcd2tm(tm);
> +
> + if (rtc_valid_tm(tm) < 0) {
> + dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + unsigned int tr, dr;
> + unsigned long irqflags;
> + int ret = 0;
> +
> + if (rtc_valid_tm(tm) < 0) {
> + dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
> + return -EINVAL;
> + }
> +
> + tm2bcd(tm);
> +
> + /* Time in BCD format */
> + tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
> + ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
> + ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR);
> +
> + /* Date in BCD format */
> + dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) |
> + ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) |
> + ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) |
> + ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY);
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + ret = stm32_rtc_enter_init_mode(rtc);
> + if (ret) {
> + dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
> + goto end;
> + }
> +
> + writel_relaxed(tr, rtc->base + STM32_RTC_TR);
> + writel_relaxed(dr, rtc->base + STM32_RTC_DR);
> +
> + stm32_rtc_exit_init_mode(rtc);
> +
> + ret = stm32_rtc_wait_sync(rtc);
> +end:
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return ret;
> +}
> +
> +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + struct rtc_time *tm = &alrm->time;
> + unsigned int alrmar, cr, isr;
> + unsigned long irqflags;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + alrmar = readl_relaxed(rtc->base + STM32_RTC_ALRMAR);
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
> + /*
> + * Date/day doesn't matter in Alarm comparison so alarm
> + * triggers every day
> + */
> + tm->tm_mday = -1;
> + tm->tm_wday = -1;
> + } else {
> + if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
> + /* Alarm is set to a day of week */
> + tm->tm_mday = -1;
> + tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
> + STM32_RTC_ALRMXR_WDAY_SHIFT;
> + tm->tm_wday %= 7;
> + } else {
> + /* Alarm is set to a day of month */
> + tm->tm_wday = -1;
> + tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
> + STM32_RTC_ALRMXR_DATE_SHIFT;
> + }
> + }
> +
> + if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
> + /* Hours don't matter in Alarm comparison */
> + tm->tm_hour = -1;
> + } else {
> + tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
> + STM32_RTC_ALRMXR_HOUR_SHIFT;
> + if (alrmar & STM32_RTC_ALRMXR_PM)
> + tm->tm_hour += 12;
> + }
> +
> + if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
> + /* Minutes don't matter in Alarm comparison */
> + tm->tm_min = -1;
> + } else {
> + tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
> + STM32_RTC_ALRMXR_MIN_SHIFT;
> + }
> +
> + if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
> + /* Seconds don't matter in Alarm comparison */
> + tm->tm_sec = -1;
> + } else {
> + tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
> + STM32_RTC_ALRMXR_SEC_SHIFT;
> + }
> +
> + bcd2tm(tm);
> +
> + alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
> + alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0;
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + unsigned long irqflags;
> + unsigned int isr, cr;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + /* We expose Alarm A to the kernel */
> + if (enabled)
> + cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
> + else
> + cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> +
> + /* Clear event irqflags, otherwise new events won't be received */
> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
> + isr &= ~STM32_RTC_ISR_ALRAF;
> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
> +
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm)
> +{
> + unsigned int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec;
> + unsigned int dr = readl_relaxed(rtc->base + STM32_RTC_DR);
> + unsigned int tr = readl_relaxed(rtc->base + STM32_RTC_TR);
> +
> + cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
> + cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
> + cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
> + cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
> + cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
> + cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
> +
> + /*
> + * Assuming current date is M-D-Y H:M:S.
> + * RTC alarm can't be set on a specific month and year.
> + * So the valid alarm range is:
> + * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S
> + * with a specific case for December...
> + */
> + if ((((tm->tm_year > cur_year) &&
> + (tm->tm_mon == 0x1) && (cur_mon == 0x12)) ||
> + ((tm->tm_year == cur_year) &&
> + (tm->tm_mon <= cur_mon + 1))) &&
> + ((tm->tm_mday < cur_day) ||
> + ((tm->tm_mday == cur_day) &&
> + ((tm->tm_hour < cur_hour) ||
> + ((tm->tm_hour == cur_hour) && (tm->tm_min < cur_min)) ||
> + ((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) &&
> + (tm->tm_sec <= cur_sec))))))
> + return 0;
> +
> + return -EINVAL;
> +}
> +
> +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + struct rtc_time *tm = &alrm->time;
> + unsigned long irqflags;
> + unsigned int cr, isr, alrmar;
> + int ret = 0;
> +
> + if (rtc_valid_tm(tm)) {
> + dev_err(dev, "Alarm time not valid.\n");
> + return -EINVAL;
> + }
> +
> + tm2bcd(tm);
> +
> + /*
> + * RTC alarm can't be set on a specific date, unless this date is
> + * up to the same day of month next month.
> + */
> + if (stm32_rtc_valid_alrm(rtc, tm) < 0) {
> + dev_err(dev, "Alarm can be set only on upcoming month.\n");
> + return -EINVAL;
> + }
> +
> + alrmar = 0;
> + /* tm_year and tm_mon are not used because not supported by RTC */
> + alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
> + STM32_RTC_ALRMXR_DATE;
> + /* 24-hour format */
> + alrmar &= ~STM32_RTC_ALRMXR_PM;
> + alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
> + STM32_RTC_ALRMXR_HOUR;
> + alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
> + STM32_RTC_ALRMXR_MIN;
> + alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
> + STM32_RTC_ALRMXR_SEC;
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + /* Disable Alarm */
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_ALRAE;
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> +
> + /*
> + * Poll Alarm write flag to be sure that Alarm update is allowed: it
> + * takes around 2 ck_rtc clock cycles
> + */
> + ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
> + isr,
> + (isr & STM32_RTC_ISR_ALRAWF),
> + 10, 100000);
> +
> + if (ret) {
> + dev_err(dev, "Alarm update not allowed\n");
> + goto end;
> + }
> +
> + /* Write to Alarm register */
> + writel_relaxed(alrmar, rtc->base + STM32_RTC_ALRMAR);
> +
> + if (alrm->enabled)
> + stm32_rtc_alarm_irq_enable(dev, 1);
> + else
> + stm32_rtc_alarm_irq_enable(dev, 0);
> +
> +end:
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return ret;
> +}
> +
> +static const struct rtc_class_ops stm32_rtc_ops = {
> + .read_time = stm32_rtc_read_time,
> + .set_time = stm32_rtc_set_time,
> + .read_alarm = stm32_rtc_read_alarm,
> + .set_alarm = stm32_rtc_set_alarm,
> + .alarm_irq_enable = stm32_rtc_alarm_irq_enable,
> +};
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id stm32_rtc_of_match[] = {
> + { .compatible = "st,stm32-rtc" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
> +#endif
> +
> +static int stm32_rtc_init(struct platform_device *pdev,
> + struct stm32_rtc *rtc)
> +{
> + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
> + unsigned int rate;
> + unsigned long irqflags;
> + int ret = 0;
> +
> + rate = clk_get_rate(rtc->ck_rtc);
> +
> + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
> + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
> + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
> +
> + for (pred_a = pred_a_max; pred_a >= 0; pred_a--) {
> + pred_s = (rate / (pred_a + 1)) - 1;
> +
> + if (((pred_s + 1) * (pred_a + 1)) == rate)
> + break;
> + }
> +
> + /*
> + * Can't find a 1Hz, so give priority to RTC power consumption
> + * by choosing the higher possible value for prediv_a
> + */
> + if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
> + pred_a = pred_a_max;
> + pred_s = (rate / (pred_a + 1)) - 1;
> +
> + dev_warn(&pdev->dev, "ck_rtc is %s\n",
> + (rate - ((pred_a + 1) * (pred_s + 1)) < 0) ?
> + "fast" : "slow");
> + }
> +
> + spin_lock_irqsave(&rtc->lock, irqflags);
> +
> + stm32_rtc_wpr_unlock(rtc);
> +
> + ret = stm32_rtc_enter_init_mode(rtc);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Can't enter in init mode. Prescaler config failed.\n");
> + goto end;
> + }
> +
> + prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
> + writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
> + prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
> + writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
> +
> + /* Force 24h time format */
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_FMT;
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> +
> + stm32_rtc_exit_init_mode(rtc);
> +
> + ret = stm32_rtc_wait_sync(rtc);
> +end:
> + stm32_rtc_wpr_lock(rtc);
> +
> + spin_unlock_irqrestore(&rtc->lock, irqflags);
> +
> + return ret;
> +}
> +
> +static int stm32_rtc_probe(struct platform_device *pdev)
> +{
> + struct stm32_rtc *rtc;
> + struct resource *res;
> + int ret;
> +
> + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
> + if (!rtc)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + rtc->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(rtc->base))
> + return PTR_ERR(rtc->base);
> +
> + dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "st,syscfg");
> + if (IS_ERR(dbp)) {
> + dev_err(&pdev->dev, "no st,syscfg\n");
> + return PTR_ERR(dbp);
> + }
> +
> + spin_lock_init(&rtc->lock);
> +
> + rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(rtc->ck_rtc)) {
> + dev_err(&pdev->dev, "no ck_rtc clock");
> + return PTR_ERR(rtc->ck_rtc);
> + }
> +
> + ret = clk_prepare_enable(rtc->ck_rtc);
> + if (ret)
> + return ret;
> +
> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
> +
> + /*
> + * After a system reset, RTC_ISR.INITS flag can be read to check if
> + * the calendar has been initalized or not. INITS flag is reset by a
> + * power-on reset (no vbat, no power-supply). It is not reset if
> + * ck_rtc parent clock has changed (so RTC prescalers need to be
> + * changed). That's why we cannot rely on this flag to know if RTC
> + * init has to be done.
> + */
> + ret = stm32_rtc_init(pdev, rtc);
> + if (ret)
> + goto err;
> +
> + rtc->irq_alarm = platform_get_irq(pdev, 0);
> + if (rtc->irq_alarm <= 0) {
> + dev_err(&pdev->dev, "no alarm irq\n");
> + ret = rtc->irq_alarm;
> + goto err;
> + }
> +
> + platform_set_drvdata(pdev, rtc);
> +
> + ret = device_init_wakeup(&pdev->dev, true);
> + if (ret)
> + dev_warn(&pdev->dev,
> + "alarm won't be able to wake up the system");
> +
> + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
> + &stm32_rtc_ops, THIS_MODULE);
> + if (IS_ERR(rtc->rtc_dev)) {
> + ret = PTR_ERR(rtc->rtc_dev);
> + dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
> + ret);
> + goto err;
> + }
> +
> + /* Handle RTC alarm interrupts */
> + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
> + stm32_rtc_alarm_irq,
> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + pdev->name, rtc);
> + if (ret) {
> + dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
> + rtc->irq_alarm);
> + goto err;
> + }
> +
> + /*
> + * If INITS flag is reset (calendar year field set to 0x00), calendar
> + * must be initialized
> + */
> + if (!(readl_relaxed(rtc->base + STM32_RTC_ISR) & STM32_RTC_ISR_INITS))
> + dev_warn(&pdev->dev, "Date/Time must be initialized\n");
> +
> + return 0;
> +err:
> + clk_disable_unprepare(rtc->ck_rtc);
> +
> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
> +
> + device_init_wakeup(&pdev->dev, false);
> +
> + return ret;
> +}
> +
> +static int __exit stm32_rtc_remove(struct platform_device *pdev)
> +{
> + struct stm32_rtc *rtc = platform_get_drvdata(pdev);
> + unsigned int cr;
> +
> + /* Disable interrupts */
> + stm32_rtc_wpr_unlock(rtc);
> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
> + cr &= ~STM32_RTC_CR_ALRAIE;
> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
> + stm32_rtc_wpr_lock(rtc);
> +
> + clk_disable_unprepare(rtc->ck_rtc);
> +
> + /* Enable backup domain write protection */
> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
> +
> + device_init_wakeup(&pdev->dev, false);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int stm32_rtc_suspend(struct device *dev)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + return enable_irq_wake(rtc->irq_alarm);
> +
> + return 0;
> +}
> +
> +static int stm32_rtc_resume(struct device *dev)
> +{
> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + ret = stm32_rtc_wait_sync(rtc);
> + if (ret < 0)
> + return ret;
> +
> + if (device_may_wakeup(dev))
> + return disable_irq_wake(rtc->irq_alarm);
> +
> + return ret;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops,
> + stm32_rtc_suspend, stm32_rtc_resume);
> +
> +static struct platform_driver stm32_rtc_driver = {
> + .probe = stm32_rtc_probe,
> + .remove = stm32_rtc_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .pm = &stm32_rtc_pm_ops,
> + .of_match_table = stm32_rtc_of_match,
> + },
> +};
> +
> +module_platform_driver(stm32_rtc_driver);
> +
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
> +MODULE_LICENSE("GPL v2");
Looks much better now.
Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
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