* [PATCH 2/3] arm64: Add Kconfig option for Samsung GH7 SoC family
From: Olof Johansson @ 2014-02-13 19:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52F99056.6010906@samsung.com>
On Mon, Feb 10, 2014 at 6:52 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> On 02/13/14 04:14, Arnd Bergmann wrote:
>>
>> On Wednesday 12 February 2014 13:04:40 Kumar Gala wrote:
>>>
>>> On Feb 12, 2014, at 12:12 PM, Catalin Marinas<catalin.marinas@arm.com>
>>> wrote:
>>>>
>>>> On 12 Feb 2014, at 16:25, Kumar Gala<galak@codeaurora.org> wrote:
>>>>>
>>>>> One reason to keep around ARCH_* is for drivers shared between arm and
>>>>> arm64 that depend on it.
>>>>
>>>>
>>>> We already converted some of them (those depending on ARCH_VEXPRESS) to
>>>> just depend on ARM64. Ideally, at some point I'd like to see them as
>>>> defaulting to modules but I don't think we are there yet (we had some
>>>> discussions at the last KS, I'm not sure anyone started looking into
>>>> this).
>>>
>>>
>>> I'm torn about this, I think for something like VEXPRESS it makes sense,
>>> however I think its reasonable to still have an config symbol for a full
>>> SoC family or something of that nature.
>>
>>
>> I think for SBSA compliant systems, we should be able to live with a
>> generic ARCH_SBSA Kconfig symbol. For more irregular embedded platforms,
>> we may need something more specific.
>>
> Basically, I agreed with Arnd's suggestion to use ARCH_SBSA. Or we need to
> define level in Kconfig like ARCH_SBSA_L1 for level1. BTW, how about
> compliant with SBSA Level1 and having some specific features?
(It's a little hard to answer since nobody can download the doc and
then talk about it.)
What kind of features are you expecting though? More IP
blocks/devices? Those are just kernel config options to enable,
ideally as modules.
x86 doesn't need config options for each generation of their platform,
and neither should ARM64. Sure, there might be drivers that don't make
sense to enable on some platforms, but that's what defconfigs (or
distro configs), and modules are for -- the modules won't load unless
the hardware is there.
As long as we're not talking about massive amounts of code that is
part of the base platform, separating out per version again doesn't
make sense -- just enable for SBSA and it'll support Level 1 through
whatever. If the kernel size becomes a concern we can revisit, but
let's not start out that way.
-Olof
^ permalink raw reply
* [PATCH v2] can: xilinx CAN controller support.
From: Marc Kleine-Budde @ 2014-02-13 19:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0dc128ac-e5cd-47bd-9ab1-3a545c3e2043@CO9EHSMHS012.ehs.local>
On 02/12/2014 08:10 AM, Kedareswara rao Appana wrote:
> This patch adds xilinx CAN controller support.
> This driver supports both ZYNQ CANPS IP and
> Soft IP AXI CAN controller.
>
> Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
> ---
> This patch is rebased on the 3.14 rc2 kernel.
> Changes for v2:
> - Updated with the review comments.
> - Removed unnecessary debug prints.
> - included tx,rx fifo depths in ZYNQ CANPS case also.
> ---
> .../devicetree/bindings/net/can/xilinx_can.txt | 45 +
> drivers/net/can/Kconfig | 7 +
> drivers/net/can/Makefile | 1 +
> drivers/net/can/xilinx_can.c | 1153 ++++++++++++++++++++
> 4 files changed, 1206 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/net/can/xilinx_can.txt
> create mode 100644 drivers/net/can/xilinx_can.c
>
> diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
> new file mode 100644
> index 0000000..0e57103
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
> @@ -0,0 +1,45 @@
> +Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
> +---------------------------------------------------------
> +
> +Required properties:
> +- compatible : Should be "xlnx,zynq-can-1.00.a" for Zynq CAN
> + controllers and "xlnx,axi-can-1.00.a" for Axi CAN
> + controllers.
> +- reg : Physical base address and size of the Axi CAN/Zynq
> + CANPS registers map.
> +- interrupts : Property with a value describing the interrupt
> + number.
> +- interrupt-parent : Must be core interrupt controller
> +- clock-names : List of input clock names - "ref_clk", "aper_clk"
> + (See clock bindings for details. Two clocks are
> + required for Zynq CAN. For Axi CAN
> + case it is one(ref_clk)).
> +- clocks : Clock phandles (see clock bindings for details).
> +- tx-fifo-depth : Can Tx fifo depth.
> +- rx-fifo-depth : Can Rx fifo depth.
> +
> +
> +Example:
> +
> +For Zynq CANPS Dts file:
> + zynq_can_0: zynq-can at e0008000 {
> + compatible = "xlnx,zynq-can-1.00.a";
> + clocks = <&clkc 19>, <&clkc 36>;
> + clock-names = "ref_clk", "aper_clk";
> + reg = <0xe0008000 0x1000>;
> + interrupts = <0 28 4>;
> + interrupt-parent = <&intc>;
> + tx-fifo-depth = <0x40>;
> + rx-fifo-depth = <0x40>;
> + };
> +For Axi CAN Dts file:
> + axi_can_0: axi-can at 40000000 {
> + compatible = "xlnx,axi-can-1.00.a";
> + clocks = <&clkc 0>;
> + clock-names = "ref_clk" ;
> + reg = <0x40000000 0x10000>;
> + interrupt-parent = <&intc>;
> + interrupts = <0 59 1>;
> + tx-fifo-depth = <0x40>;
> + rx-fifo-depth = <0x40>;
> + };
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 9e7d95d..b180239 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -125,6 +125,13 @@ config CAN_GRCAN
> endian syntheses of the cores would need some modifications on
> the hardware level to work.
>
> +config CAN_XILINXCAN
> + tristate "Xilinx CAN"
> + depends on ARCH_ZYNQ || MICROBLAZE
> + ---help---
> + Xilinx CAN driver. This driver supports both soft AXI CAN IP and
> + Zynq CANPS IP.
> +
> source "drivers/net/can/mscan/Kconfig"
>
> source "drivers/net/can/sja1000/Kconfig"
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index c744039..0b8e11e 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
> obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
> obj-$(CONFIG_PCH_CAN) += pch_can.o
> obj-$(CONFIG_CAN_GRCAN) += grcan.o
> +obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
>
> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
> new file mode 100644
> index 0000000..642e6b4
> --- /dev/null
> +++ b/drivers/net/can/xilinx_can.c
> @@ -0,0 +1,1153 @@
> +/* Xilinx CAN device driver
> + *
> + * Copyright (C) 2012 - 2014 Xilinx, Inc.
> + * Copyright (C) 2009 PetaLogix. All rights reserved.
> + *
> + * Description:
> + * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/skbuff.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +#include <linux/can/led.h>
> +
> +#define DRIVER_NAME "XILINX_CAN"
> +
> +/* CAN registers set */
> +#define XCAN_SRR_OFFSET 0x00 /* Software reset */
> +#define XCAN_MSR_OFFSET 0x04 /* Mode select */
> +#define XCAN_BRPR_OFFSET 0x08 /* Baud rate prescaler */
> +#define XCAN_BTR_OFFSET 0x0C /* Bit timing */
> +#define XCAN_ECR_OFFSET 0x10 /* Error counter */
> +#define XCAN_ESR_OFFSET 0x14 /* Error status */
> +#define XCAN_SR_OFFSET 0x18 /* Status */
> +#define XCAN_ISR_OFFSET 0x1C /* Interrupt status */
> +#define XCAN_IER_OFFSET 0x20 /* Interrupt enable */
> +#define XCAN_ICR_OFFSET 0x24 /* Interrupt clear */
> +#define XCAN_TXFIFO_ID_OFFSET 0x30 /* TX FIFO ID */
> +#define XCAN_TXFIFO_DLC_OFFSET 0x34 /* TX FIFO DLC */
> +#define XCAN_TXFIFO_DW1_OFFSET 0x38 /* TX FIFO Data Word 1 */
> +#define XCAN_TXFIFO_DW2_OFFSET 0x3C /* TX FIFO Data Word 2 */
> +#define XCAN_RXFIFO_ID_OFFSET 0x50 /* RX FIFO ID */
> +#define XCAN_RXFIFO_DLC_OFFSET 0x54 /* RX FIFO DLC */
> +#define XCAN_RXFIFO_DW1_OFFSET 0x58 /* RX FIFO Data Word 1 */
> +#define XCAN_RXFIFO_DW2_OFFSET 0x5C /* RX FIFO Data Word 2 */
Can you define all register offsets via an enum please.
> +/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
> +#define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */
> +#define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */
> +#define XCAN_MSR_LBACK_MASK 0x00000002 /* Loop back mode select */
> +#define XCAN_MSR_SLEEP_MASK 0x00000001 /* Sleep mode select */
> +#define XCAN_BRPR_BRP_MASK 0x000000FF /* Baud rate prescaler */
> +#define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */
> +#define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */
> +#define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */
> +#define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */
> +#define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */
> +#define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */
> +#define XCAN_ESR_BERR_MASK 0x00000008 /* Bit error */
> +#define XCAN_ESR_STER_MASK 0x00000004 /* Stuff error */
> +#define XCAN_ESR_FMER_MASK 0x00000002 /* Form error */
> +#define XCAN_ESR_CRCER_MASK 0x00000001 /* CRC error */
> +#define XCAN_SR_TXFLL_MASK 0x00000400 /* TX FIFO is full */
> +#define XCAN_SR_ESTAT_MASK 0x00000180 /* Error status */
> +#define XCAN_SR_ERRWRN_MASK 0x00000040 /* Error warning */
> +#define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */
> +#define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */
> +#define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */
> +#define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */
> +#define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */
> +#define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */
> +#define XCAN_IXR_BSOFF_MASK 0x00000200 /* Bus off interrupt */
> +#define XCAN_IXR_ERROR_MASK 0x00000100 /* Error interrupt */
> +#define XCAN_IXR_RXNEMP_MASK 0x00000080 /* RX FIFO NotEmpty intr */
> +#define XCAN_IXR_RXOFLW_MASK 0x00000040 /* RX FIFO Overflow intr */
> +#define XCAN_IXR_RXOK_MASK 0x00000010 /* Message received intr */
> +#define XCAN_IXR_TXOK_MASK 0x00000002 /* TX successful intr */
> +#define XCAN_IXR_ARBLST_MASK 0x00000001 /* Arbitration lost intr */
> +#define XCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */
> +#define XCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */
> +#define XCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */
> +#define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */
> +#define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */
> +#define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */
> +
> +#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
> + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
> + XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
> + XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
> +
> +/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
> +#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
> +#define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */
> +#define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */
> +#define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */
> +#define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */
> +#define XCAN_ESR_REC_SHIFT 8 /* Rx Error Count */
> +
> +/* CAN frame length constants */
> +#define XCAN_ECHO_SKB_MAX 64
> +#define XCAN_FRAME_MAX_DATA_LEN 8
> +#define XCAN_TIMEOUT (50 * HZ)
This is 50 seconds, is this intentional?
> +
> +/**
> + * struct xcan_priv - This definition define CAN driver instance
> + * @can: CAN private data structure.
> + * @open_time: For holding timeout values
Please remove open_time completely from the driver.
> + * @waiting_ech_skb_index: Pointer for skb
> + * @ech_skb_next: This tell the next packet in the queue
> + * @waiting_ech_skb_num: Gives the number of packets waiting
> + * @xcan_echo_skb_max_tx: Maximum number packets the driver can send
> + * @xcan_echo_skb_max_rx: Maximum number packets the driver can receive
> + * @napi: NAPI structure
> + * @ech_skb_lock: For spinlock purpose
> + * @read_reg: For reading data from CAN registers
> + * @write_reg: For writing data to CAN registers
> + * @dev: Network device data structure
> + * @reg_base: Ioremapped address to registers
> + * @irq_flags: For request_irq()
> + * @aperclk: Pointer to struct clk
> + * @devclk: Pointer to struct clk
> + */
> +struct xcan_priv {
> + struct can_priv can;
> + int open_time;
> + int waiting_ech_skb_index;
> + int ech_skb_next;
please make them:
unsigned int tx_head;
unsigned int tx_tail;
I'll explain how to use them later. Have a look at the ti_hecc driver.
> + int waiting_ech_skb_num;
> + int xcan_echo_skb_max_tx;
> + int xcan_echo_skb_max_rx;
> + struct napi_struct napi;
> + spinlock_t ech_skb_lock;
> + u32 (*read_reg)(const struct xcan_priv *priv, int reg);
> + void (*write_reg)(const struct xcan_priv *priv, int reg, u32 val);
Please remove read_reg, write_reg, as long as there isn't any BE support
in the driver, call them directly.
> + struct net_device *dev;
> + void __iomem *reg_base;
> + unsigned long irq_flags;
> + struct clk *aperclk;
> + struct clk *devclk;
> +};
> +
> +/* CAN Bittiming constants as per Xilinx CAN specs */
> +static const struct can_bittiming_const xcan_bittiming_const = {
> + .name = DRIVER_NAME,
> + .tseg1_min = 1,
> + .tseg1_max = 16,
> + .tseg2_min = 1,
> + .tseg2_max = 8,
> + .sjw_max = 4,
> + .brp_min = 1,
> + .brp_max = 256,
> + .brp_inc = 1,
> +};
> +
> +/**
> + * xcan_write_reg - Write a value to the device register
> + * @priv: Driver private data structure
> + * @reg: Register offset
> + * @val: Value to write at the Register offset
> + *
> + * Write data to the paricular CAN register
> + */
> +static void xcan_write_reg(const struct xcan_priv *priv, int reg, u32 val)
Please use the enum for instead of an int for the reg.
> +{
> + writel(val, priv->reg_base + reg);
> +}
> +
> +/**
> + * xcan_read_reg - Read a value from the device register
> + * @priv: Driver private data structure
> + * @reg: Register offset
> + *
> + * Read data from the particular CAN register
> + * Return: value read from the CAN register
> + */
> +static u32 xcan_read_reg(const struct xcan_priv *priv, int reg)
> +{
same here
> + return readl(priv->reg_base + reg);
> +}
> +
> +/**
> + * set_reset_mode - Resets the CAN device mode
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver reset mode routine.The driver
> + * enters into configuration mode.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int set_reset_mode(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + unsigned long timeout;
> +
> + priv->can.state = CAN_STATE_STOPPED;
> +
> + timeout = jiffies + XCAN_TIMEOUT;
> + while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
> + if (time_after(jiffies, timeout)) {
> + netdev_warn(ndev, "timedout waiting for config mode\n");
> + return -ETIMEDOUT;
> + }
> + usleep_range(500, 10000);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_set_bittiming - CAN set bit timing routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver set bittiming routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_set_bittiming(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct can_bittiming *bt = &priv->can.bittiming;
> + u32 btr0, btr1;
> + u32 is_config_mode;
> +
> + /* Check whether Xilinx CAN is in configuration mode.
> + * It cannot set bit timing if Xilinx CAN is not in configuration mode.
> + */
> + is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
> + XCAN_SR_CONFIG_MASK;
> + if (!is_config_mode) {
> + netdev_alert(ndev,
> + "Cannot set bittiming can is not in config mode\n");
> + return -EPERM;
> + }
> +
> + /* Setting Baud Rate prescalar value in BRPR Register */
> + btr0 = (bt->brp - 1) & XCAN_BRPR_BRP_MASK;
> +
> + /* Setting Time Segment 1 in BTR Register */
> + btr1 = (bt->prop_seg + bt->phase_seg1 - 1) & XCAN_BTR_TS1_MASK;
> +
> + /* Setting Time Segment 2 in BTR Register */
> + btr1 |= ((bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT) &
> + XCAN_BTR_TS2_MASK;
> +
> + /* Setting Synchronous jump width in BTR Register */
> + btr1 |= ((bt->sjw - 1) << XCAN_BTR_SJW_SHIFT) & XCAN_BTR_SJW_MASK;
All the masking should not be needed, as the bit timing is calculated
within the bounds you specified.
> + priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
> + priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
> +
> + netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
> + priv->read_reg(priv, XCAN_BRPR_OFFSET),
> + priv->read_reg(priv, XCAN_BTR_OFFSET));
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_start - This the drivers start routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers start routine.
> + * Based on the State of the CAN device it puts
> + * the CAN device into a proper mode.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_start(struct net_device *ndev)
Please name the function xcan_chip_start(), to for a common naming like
the flexcan and at91 driver.
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 err;
> + unsigned long timeout;
> +
> + /* Check if it is in reset mode */
> + if (priv->can.state != CAN_STATE_STOPPED)
Don't depend on any state here, I suggest to do a softreset (or
equivalent) of you CAN core and configure everything.
> + err = set_reset_mode(ndev);
> + if (err < 0)
> + return err;
> +
> + /* Enable interrupts */
> + priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
> +
> + /* Check whether it is loopback mode or normal mode */
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
> + /* Put device into loopback mode */
> + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_LBACK_MASK);
> + else
> + /* The device is in normal mode */
> + priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
> +
> + if (priv->can.state == CAN_STATE_STOPPED) {
> + /* Enable Xilinx CAN */
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + timeout = jiffies + XCAN_TIMEOUT;
> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
> + while ((priv->read_reg(priv, XCAN_SR_OFFSET)
> + & XCAN_SR_LBACK_MASK) == 0) {
> + if (time_after(jiffies, timeout)) {
> + netdev_warn(ndev,
> + "timedout for loopback mode\n");
> + return -ETIMEDOUT;
> + }
> + usleep_range(500, 10000);
> + }
> + } else {
> + while ((priv->read_reg(priv, XCAN_SR_OFFSET)
> + & XCAN_SR_NORMAL_MASK) == 0) {
> + if (time_after(jiffies, timeout)) {
> + netdev_warn(ndev,
> + "timedout for normal mode\n");
> + return -ETIMEDOUT;
> + }
> + usleep_range(500, 10000);
> + }
> + }
> + netdev_dbg(ndev, "status:#x%08x\n",
> + priv->read_reg(priv, XCAN_SR_OFFSET));
> + }
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> + return 0;
> +}
> +
> +/**
> + * xcan_do_set_mode - This sets the mode of the driver
> + * @ndev: Pointer to net_device structure
> + * @mode: Tells the mode of the driver
> + *
> + * This check the drivers state and calls the
> + * the corresponding modes to set.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
> +{
> + int ret;
> +
> + switch (mode) {
> + case CAN_MODE_START:
> + ret = xcan_start(ndev);
> + if (ret < 0)
> + netdev_err(ndev, "xcan_start failed!\n");
> + netif_wake_queue(ndev);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * xcan_start_xmit - Starts the transmission
> + * @skb: sk_buff pointer that contains data to be Txed
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from upper layers to initiate transmission. This
> + * function uses the next available free txbuff and populates their fields to
> + * start the transmission.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf = (struct can_frame *)skb->data;
> + u32 id, dlc, data[2] = {0, 0}, rtr = 0;
I think you can drop the rtr varibale and use cf->can_id & CAN_RTR_FLAG
instead.
> + unsigned long flags;
> +
> + if (can_dropped_invalid_skb(ndev, skb))
> + return NETDEV_TX_OK;
> +
> + /* Watch carefully on the bit sequence */
> + if (cf->can_id & CAN_EFF_FLAG) {
> + /* Extended CAN ID format */
> + id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
> + XCAN_IDR_ID2_MASK;
> + id |= (((cf->can_id & CAN_EFF_MASK) >>
> + (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
> + XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
> +
> + /* The substibute remote TX request bit should be "1"
> + * for extended frames as in the Xilinx CAN datasheet
> + */
> + id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
> +
> + if (cf->can_id & CAN_RTR_FLAG) {
> + /* Extended frames remote TX request */
> + id |= XCAN_IDR_RTR_MASK;
> + rtr = 1;
> + }
> + } else {
> + /* Standard CAN ID format */
> + id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
> + XCAN_IDR_ID1_MASK;
> +
> + if (cf->can_id & CAN_RTR_FLAG) {
> + /* Extended frames remote TX request */
> + id |= XCAN_IDR_SRR_MASK;
> + rtr = 1;
> + }
> + }
> +
> + dlc = (cf->can_dlc & 0xf) << XCAN_DLCR_DLC_SHIFT;
No need to mask dlc, it's valid.
> +
> + if (dlc > 0)
You've copied my speudo code :)
But you have to use (cf->can_dlc > 0) here, as dlc is the shifted value.
> + data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
> + if (dlc > 4)
> + data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
> +
> + can_put_echo_skb(skb, ndev, priv->ech_skb_next);
can_put_echo_skb(skb, ndev,
priv->tx_head % priv->xcan_echo_skb_max_tx);
priv->tx_head++;
> +
> + /* Write the Frame to Xilinx CAN TX FIFO */
> + priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
> + priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
> + if (!rtr) {
> + priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data[0]);
> + priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data[1]);
> + stats->tx_bytes += cf->can_dlc;
Please add a comment which write triggers the tx. What in case of the
rtr? Which write triggers the tx then?
> + }
> +
> + priv->ech_skb_next = (priv->ech_skb_next + 1) %
> + priv->xcan_echo_skb_max_tx;
Please remove, it's not needed.
> +
> + spin_lock_irqsave(&priv->ech_skb_lock, flags);
> + priv->waiting_ech_skb_num++;
> + spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
All 3 not needed.
> +
> + /* Check if the TX buffer is full */
> + if (priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_TXFLL_MASK) {
> + netif_stop_queue(ndev);
> + netdev_err(ndev, "TX register is still full!\n");
> + return NETDEV_TX_BUSY;
If this is true, there is a Bug in the flow control. It should be moved
to the beginning of the function, see at91_can's xmit function.
/* Check if the TX buffer is full */
if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) &
XCAN_SR_TXFLL_MASK)) {
netif_stop_queue(ndev);
netdev_err(ndev,
"BUG!, TX FIFO full when queue awake!\n");
return NETDEV_TX_BUSY;
}
> + } else if (priv->waiting_ech_skb_num == priv->xcan_echo_skb_max_tx) {
> + netif_stop_queue(ndev);
> + netdev_err(ndev, "waiting:0x%08x, max:0x%08x\n",
> + priv->waiting_ech_skb_num, priv->xcan_echo_skb_max_tx);
> + return NETDEV_TX_BUSY;
> + }
This is a the regular flow control function and must be called before a
TX complete interrupt can trigger. Your tx-complete interrupt is
probably always enabled?
So here you check the fill level of the FIFO:
if ((priv->tx_head - priv->tx_tail) ==
priv->xcan_echo_skb_max_tx)
netif_stop_queue(ndev);
If it's full, stop the queue. The you trigger the tx, the tx complete
interrupt gets called and the queue will be restarted.
> +
> + return NETDEV_TX_OK;
> +}
> +
> +/**
> + * xcan_rx - Is called from CAN isr to complete the received
> + * frame processing
> + * @ndev: Pointer to net_device structure
> + *
> + * This function is invoked from the CAN isr(poll) to process the Rx frames. It
> + * does minimal processing and invokes "netif_receive_skb" to complete further
> + * processing.
> + * Return: 0 on success and negative error value on error
> + */
> +static int xcan_rx(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + u32 id_xcan, dlc, data[2] = {0, 0}, rtr = 0;
> +
> + skb = alloc_can_skb(ndev, &cf);
> + if (!skb)
> + return -ENOMEM;
> +
> + /* Read a frame from Xilinx zynq CANPS */
> + id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
> + dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) & XCAN_DLCR_DLC_MASK;
Better do the shift to dlc.
> +
> + /* Change Xilinx CAN data length format to socketCAN data format */
> + cf->can_dlc = get_can_dlc((dlc & XCAN_DLCR_DLC_MASK) >>
> + XCAN_DLCR_DLC_SHIFT);
Then it's just: get_can_dlc(dlc);
> +
> + /* Change Xilinx CAN ID format to socketCAN ID format */
> + if (id_xcan & XCAN_IDR_IDE_MASK) {
> + /* The received frame is an Extended format frame */
> + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
> + cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
> + XCAN_IDR_ID2_SHIFT;
> + cf->can_id |= CAN_EFF_FLAG;
> + if (id_xcan & XCAN_IDR_RTR_MASK) {
> + cf->can_id |= CAN_RTR_FLAG;
> + rtr = 1;
> + }
> + } else {
> + /* The received frame is a standard format frame */
> + cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
> + XCAN_IDR_ID1_SHIFT;
> + if (id_xcan & XCAN_IDR_RTR_MASK) {
> + cf->can_id |= CAN_RTR_FLAG;
> + rtr = 1;
> + }
> + }
> +
> + if (!rtr) {
> + data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
> + data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
> +
> + /* Change Xilinx CAN data format to socketCAN data format */
> + *(__be32 *)(cf->data) = cpu_to_be32(data[0]);
> + if (cf->can_dlc > 4)
> + *(__be32 *)(cf->data + 4) = cpu_to_be32(data[1]);
> + }
> + can_led_event(ndev, CAN_LED_EVENT_RX);
> +
> + netif_receive_skb(skb);
> +
> + stats->rx_bytes += cf->can_dlc;
> + stats->rx_packets++;
> + return 0;
> +}
> +
> +/**
> + * xcan_err_interrupt - error frame Isr
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This is the CAN error interrupt and it will
> + * check the the type of error and forward the error
> + * frame to upper layers.
> + */
> +static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + struct can_frame *cf;
> + struct sk_buff *skb;
> + u32 err_status, status;
> +
> + skb = alloc_can_err_skb(ndev, &cf);
> + if (!skb) {
> + netdev_err(ndev, "alloc_can_err_skb() failed!\n");
> + return;
> + }
> +
> + err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
> + priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
> + status = priv->read_reg(priv, XCAN_SR_OFFSET);
> +
> + if (isr & XCAN_IXR_BSOFF_MASK) {
> + priv->can.state = CAN_STATE_BUS_OFF;
> + cf->can_id |= CAN_ERR_BUSOFF;
> + priv->can.can_stats.bus_off++;
> + /* Leave device in Config Mode in bus-off state */
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + can_bus_off(ndev);
> + } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + priv->can.state = CAN_STATE_ERROR_PASSIVE;
> + priv->can.can_stats.error_passive++;
> + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE |
> + CAN_ERR_CRTL_TX_PASSIVE;
> + } else if (status & XCAN_SR_ERRWRN_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + priv->can.state = CAN_STATE_ERROR_WARNING;
> + priv->can.can_stats.error_warning++;
> + cf->data[1] |= CAN_ERR_CRTL_RX_WARNING |
> + CAN_ERR_CRTL_TX_WARNING;
> + }
> +
> + /* Check for Arbitration lost interrupt */
> + if (isr & XCAN_IXR_ARBLST_MASK) {
> + cf->can_id |= CAN_ERR_LOSTARB;
> + cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
> + priv->can.can_stats.arbitration_lost++;
> + }
> +
> + /* Check for RX FIFO Overflow interrupt */
> + if (isr & XCAN_IXR_RXOFLW_MASK) {
> + cf->can_id |= CAN_ERR_CRTL;
> + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> + stats->rx_over_errors++;
> + stats->rx_errors++;
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + }
> +
> + /* Check for error interrupt */
> + if (isr & XCAN_IXR_ERROR_MASK) {
> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> + cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +
> + /* Check for Ack error interrupt */
> + if (err_status & XCAN_ESR_ACKER_MASK) {
> + cf->can_id |= CAN_ERR_ACK;
> + cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
> + stats->tx_errors++;
> + }
> +
> + /* Check for Bit error interrupt */
> + if (err_status & XCAN_ESR_BERR_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_BIT;
> + stats->tx_errors++;
> + }
> +
> + /* Check for Stuff error interrupt */
> + if (err_status & XCAN_ESR_STER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_STUFF;
> + stats->rx_errors++;
> + }
> +
> + /* Check for Form error interrupt */
> + if (err_status & XCAN_ESR_FMER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[2] = CAN_ERR_PROT_FORM;
> + stats->rx_errors++;
> + }
> +
> + /* Check for CRC error interrupt */
> + if (err_status & XCAN_ESR_CRCER_MASK) {
> + cf->can_id |= CAN_ERR_PROT;
> + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
> + CAN_ERR_PROT_LOC_CRC_DEL;
> + stats->rx_errors++;
> + }
> + priv->can.can_stats.bus_error++;
> + }
> +
> + netif_rx(skb);
> + stats->rx_packets++;
> + stats->rx_bytes += cf->can_dlc;
> +
> + netdev_dbg(ndev, "%s: error status register:0x%x\n",
> + __func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
> +}
> +
> +/**
> + * xcan_state_interrupt - It will check the state of the CAN device
> + * @ndev: net_device pointer
> + * @isr: interrupt status register value
> + *
> + * This will checks the state of the CAN device
> + * and puts the device into appropriate state.
> + */
> +static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + /* Check for Sleep interrupt if set put CAN device in sleep state */
> + if (isr & XCAN_IXR_SLP_MASK)
> + priv->can.state = CAN_STATE_SLEEPING;
> +
> + /* Check for Wake up interrupt if set put CAN device in Active state */
> + if (isr & XCAN_IXR_WKUP_MASK)
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +}
> +
> +/**
> + * xcan_rx_poll - Poll routine for rx packets (NAPI)
> + * @napi: napi structure pointer
> + * @quota: Max number of rx packets to be processed.
> + *
> + * This is the poll routine for rx part.
> + * It will process the packets maximux quota value.
> + *
> + * Return: number of packets received
> + */
> +static int xcan_rx_poll(struct napi_struct *napi, int quota)
> +{
> + struct net_device *ndev = napi->dev;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 isr, ier;
> + int work_done = 0;
> +
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
> + if (isr & XCAN_IXR_RXOK_MASK) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET,
> + XCAN_IXR_RXOK_MASK);
> + if (xcan_rx(ndev) < 0)
> + return work_done;
> + work_done++;
> + } else {
> + priv->write_reg(priv, XCAN_ICR_OFFSET,
> + XCAN_IXR_RXNEMP_MASK);
> + break;
> + }
What does the XCAN_IXR_RXOK_MASK mean if it's send and undset?
> + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + }
> +
> + if (work_done < quota) {
> + napi_complete(napi);
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
Is this a read-modify-write register? I mean will an interrupt get
disabled, if you write a 0-bit in the IER register? What does the ICR
register?
> + }
> + return work_done;
> +}
> +
> +/**
> + * xcan_tx_interrupt - Tx Done Isr
> + * @ndev: net_device pointer
> + */
> +static void xcan_tx_interrupt(struct net_device *ndev)
> +{
> + unsigned long flags;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + struct net_device_stats *stats = &ndev->stats;
> + u32 processed = 0, txpackets;
> +
> + stats->tx_packets++;
> + netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
> + priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
> +
> + txpackets = priv->waiting_ech_skb_num;
> +
> + if (txpackets) {
> + can_get_echo_skb(ndev, priv->waiting_ech_skb_index);
> + priv->waiting_ech_skb_index =
> + (priv->waiting_ech_skb_index + 1) %
> + priv->xcan_echo_skb_max_tx;
> + processed++;
> + txpackets--;
> + }
> +
> + spin_lock_irqsave(&priv->ech_skb_lock, flags);
> + priv->waiting_ech_skb_num -= processed;
> + spin_unlock_irqrestore(&priv->ech_skb_lock, flags);
This all simplyfies to a:
can_get_echo_skb(ndev, priv->tx_tail %
priv->xcan_echo_skb_max_tx);
priv->tx_tail++;
I think you should add some kind of loop here, it there is more than one
tx-complete per IRQ.
> +
> + netdev_dbg(ndev, "%s: waiting total:%d,current:%d\n", __func__,
> + priv->waiting_ech_skb_num, priv->waiting_ech_skb_index);
> +
> + netif_wake_queue(ndev);
> +
> + can_led_event(ndev, CAN_LED_EVENT_TX);
> +}
> +
> +/**
> + * xcan_interrupt - CAN Isr
> + * @irq: irq number
> + * @dev_id: device id poniter
> + *
> + * This is the xilinx CAN Isr. It checks for the type of interrupt
> + * and invokes the corresponding ISR.
> + *
> + * Return:
> + * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
> + */
> +static irqreturn_t xcan_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *)dev_id;
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 isr, ier;
> +
> + /* Get the interrupt status from Xilinx CAN */
> + isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
> + if (!isr)
> + return IRQ_NONE;
> +
> + netdev_dbg(ndev, "%s: isr:#x%08x, err:#x%08x\n", __func__,
> + isr, priv->read_reg(priv, XCAN_ESR_OFFSET));
> +
> + /* Check for the type of interrupt and Processing it */
> + if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
> + XCAN_IXR_WKUP_MASK));
> + xcan_state_interrupt(ndev, isr);
> + }
> +
> + /* Check for Tx interrupt and Processing it */
> + if (isr & XCAN_IXR_TXOK_MASK) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
> + xcan_tx_interrupt(ndev);
> + }
> +
> + /* Check for the type of error interrupt and Processing it */
> + if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
> + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
> + priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
> + XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
> + XCAN_IXR_ARBLST_MASK));
> + xcan_err_interrupt(ndev, isr);
> + }
> +
> + /* Check for the type of receive interrupt and Processing it */
> + if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + napi_schedule(&priv->napi);
> + }
> + return IRQ_HANDLED;
> +}
> +
> +/**
> + * xcan_stop - Driver stop routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the drivers stop routine. It will disable the
> + * interrupts and put the device into configuration mode.
> + */
> +static void xcan_stop(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + u32 ier;
> +
> + /* Disable interrupts and leave the can in configuration mode */
> + ier = priv->read_reg(priv, XCAN_IER_OFFSET);
> + ier &= ~XCAN_INTR_ALL;
> + priv->write_reg(priv, XCAN_IER_OFFSET, ier);
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
> + priv->can.state = CAN_STATE_STOPPED;
> +}
> +
> +/**
> + * xcan_open - Driver open routine
> + * @ndev: Pointer to net_device structure
> + *
> + * This is the driver open routine.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_open(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int ret;
> +
> + ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
> + ndev->name, (void *)ndev);
> + if (ret < 0) {
> + netdev_err(ndev, "Irq allocation for CAN failed\n");
> + return ret;
> + }
> +
> + /* Set chip into reset mode */
> + ret = set_reset_mode(ndev);
> + if (ret < 0)
> + netdev_err(ndev, "mode resetting failed failed!\n");
Is this critical?
> +
> + /* Common open */
> + ret = open_candev(ndev);
> + if (ret)
> + return ret;
You should free the interrupt handler if this fails.
> +
> + ret = xcan_start(ndev);
> + if (ret < 0)
> + netdev_err(ndev, "xcan_start failed!\n");
> +
> +
> + can_led_event(ndev, CAN_LED_EVENT_OPEN);
> + napi_enable(&priv->napi);
> + netif_start_queue(ndev);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_close - Driver close routine
> + * @ndev: Pointer to net_device structure
> + *
> + * Return: 0 always
> + */
> +static int xcan_close(struct net_device *ndev)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + netif_stop_queue(ndev);
> + napi_disable(&priv->napi);
> + xcan_stop(ndev);
> + free_irq(ndev->irq, ndev);
> + close_candev(ndev);
> +
> + can_led_event(ndev, CAN_LED_EVENT_STOP);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_get_berr_counter - error counter routine
> + * @ndev: Pointer to net_device structure
> + * @bec: Pointer to can_berr_counter structure
> + *
> + * This is the driver error counter routine.
> + * Return: 0 always
> + */
> +static int xcan_get_berr_counter(const struct net_device *ndev,
> + struct can_berr_counter *bec)
> +{
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
> + bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
> + XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
> + return 0;
> +}
> +
> +static const struct net_device_ops xcan_netdev_ops = {
> + .ndo_open = xcan_open,
> + .ndo_stop = xcan_close,
> + .ndo_start_xmit = xcan_start_xmit,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/**
> + * xcan_suspend - Suspend method for the driver
> + * @_dev: Address of the platform_device structure
> + *
> + * Put the driver into low power mode.
> + * Return: 0 always
> + */
> +static int xcan_suspend(struct device *_dev)
> +{
> + struct platform_device *pdev = container_of(_dev,
> + struct platform_device, dev);
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + if (netif_running(ndev)) {
> + netif_stop_queue(ndev);
> + netif_device_detach(ndev);
> + }
> +
> + priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
> + priv->can.state = CAN_STATE_SLEEPING;
> +
> + clk_disable(priv->aperclk);
> + clk_disable(priv->devclk);
> +
> + return 0;
> +}
> +
> +/**
> + * xcan_resume - Resume from suspend
> + * @dev: Address of the platformdevice structure
> + *
> + * Resume operation after suspend.
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_resume(struct device *dev)
> +{
> + struct platform_device *pdev = container_of(dev,
> + struct platform_device, dev);
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> + int ret;
> +
> + ret = clk_enable(priv->aperclk);
> + if (ret) {
> + dev_err(dev, "Cannot enable clock.\n");
> + return ret;
> + }
> + ret = clk_enable(priv->devclk);
> + if (ret) {
> + dev_err(dev, "Cannot enable clock.\n");
> + return ret;
> + }
> +
> + priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
> + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> + if (netif_running(ndev)) {
> + netif_device_attach(ndev);
> + netif_start_queue(ndev);
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
> +
> +/**
> + * xcan_probe - Platform registration call
> + * @pdev: Handle to the platform device structure
> + *
> + * This function does all the memory allocation and registration for the CAN
> + * device.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int xcan_probe(struct platform_device *pdev)
> +{
> + struct resource *res; /* IO mem resources */
> + struct net_device *ndev;
> + struct xcan_priv *priv;
> + int ret, fifodep;
> +
> + /* Create a CAN device instance */
> + ndev = alloc_candev(sizeof(struct xcan_priv), XCAN_ECHO_SKB_MAX);
> + if (!ndev)
> + return -ENOMEM;
> +
> + priv = netdev_priv(ndev);
> + priv->dev = ndev;
> + priv->can.bittiming_const = &xcan_bittiming_const;
> + priv->can.do_set_bittiming = xcan_set_bittiming;
> + priv->can.do_set_mode = xcan_do_set_mode;
> + priv->can.do_get_berr_counter = xcan_get_berr_counter;
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
> + CAN_CTRLMODE_BERR_REPORTING;
> +
> + /* Get IRQ for the device */
> + ndev->irq = platform_get_irq(pdev, 0);
> +
> + spin_lock_init(&priv->ech_skb_lock);
> + ndev->flags |= IFF_ECHO; /* We support local echo */
> +
> + platform_set_drvdata(pdev, ndev);
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> + ndev->netdev_ops = &xcan_netdev_ops;
> +
> + /* Get the virtual base address for the device */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->reg_base)) {
> + ret = PTR_ERR(priv->reg_base);
> + goto err_free;
> + }
> + ndev->mem_start = res->start;
> + ndev->mem_end = res->end;
> +
> + priv->write_reg = xcan_write_reg;
> + priv->read_reg = xcan_read_reg;
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth",
> + &fifodep);
> + if (ret < 0)
> + goto err_free;
> + priv->xcan_echo_skb_max_tx = fifodep;
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth",
> + &fifodep);
> + if (ret < 0)
> + goto err_free;
> + priv->xcan_echo_skb_max_rx = fifodep;
> +
> + /* Getting the CAN devclk info */
> + priv->devclk = devm_clk_get(&pdev->dev, "ref_clk");
> + if (IS_ERR(priv->devclk)) {
> + dev_err(&pdev->dev, "Device clock not found.\n");
> + ret = PTR_ERR(priv->devclk);
> + goto err_free;
> + }
> +
> + /* Check for type of CAN device */
> + if (of_device_is_compatible(pdev->dev.of_node,
> + "xlnx,zynq-can-1.00.a")) {
> + priv->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
> + if (IS_ERR(priv->aperclk)) {
> + dev_err(&pdev->dev, "aper clock not found\n");
> + ret = PTR_ERR(priv->aperclk);
> + goto err_free;
> + }
> + } else {
> + priv->aperclk = priv->devclk;
> + }
> +
> + ret = clk_prepare_enable(priv->devclk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable device clock\n");
> + goto err_free;
> + }
> +
> + ret = clk_prepare_enable(priv->aperclk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable aper clock\n");
> + goto err_unprepar_disabledev;
> + }
Can you keep your clocks disaled if the interface is not up?
> +
> + priv->can.clock.freq = clk_get_rate(priv->devclk);
> +
> + netif_napi_add(ndev, &priv->napi, xcan_rx_poll,
> + priv->xcan_echo_skb_max_rx);
> + ret = register_candev(ndev);
> + if (ret) {
> + dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
> + goto err_unprepar_disableaper;
> + }
> +
> + devm_can_led_init(ndev);
> + dev_info(&pdev->dev,
> + "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
> + priv->reg_base, ndev->irq, priv->can.clock.freq,
> + priv->xcan_echo_skb_max_tx);
> +
> + return 0;
> +
> +err_unprepar_disableaper:
> + clk_disable_unprepare(priv->aperclk);
> +err_unprepar_disabledev:
> + clk_disable_unprepare(priv->devclk);
> +err_free:
> + free_candev(ndev);
> +
> + return ret;
> +}
> +
> +/**
> + * xcan_remove - Unregister the device after releasing the resources
> + * @pdev: Handle to the platform device structure
> + *
> + * This function frees all the resources allocated to the device.
> + * Return: 0 always
> + */
> +static int xcan_remove(struct platform_device *pdev)
> +{
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct xcan_priv *priv = netdev_priv(ndev);
> +
> + if (set_reset_mode(ndev) < 0)
> + netdev_err(ndev, "mode resetting failed!\n");
> +
> + unregister_candev(ndev);
> + netif_napi_del(&priv->napi);
> + clk_disable_unprepare(priv->aperclk);
> + clk_disable_unprepare(priv->devclk);
> +
> + free_candev(ndev);
> +
> + return 0;
> +}
> +
> +/* Match table for OF platform binding */
> +static struct of_device_id xcan_of_match[] = {
> + { .compatible = "xlnx,zynq-can-1.00.a", },
> + { .compatible = "xlnx,axi-can-1.00.a", },
> + { /* end of list */ },
> +};
> +MODULE_DEVICE_TABLE(of, xcan_of_match);
> +
> +static struct platform_driver xcan_driver = {
> + .probe = xcan_probe,
> + .remove = xcan_remove,
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = DRIVER_NAME,
> + .pm = &xcan_dev_pm_ops,
> + .of_match_table = xcan_of_match,
> + },
> +};
> +
> +module_platform_driver(xcan_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xilinx Inc");
> +MODULE_DESCRIPTION("Xilinx CAN interface");
>
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 242 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140213/a7cbc055/attachment-0001.sig>
^ permalink raw reply
* [PATCH 1/3] driver core & of: Mark of_nodes of added device as populated
From: Rob Herring @ 2014-02-13 19:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392137610-27842-2-git-send-email-pawel.moll@arm.com>
On Tue, Feb 11, 2014 at 10:53 AM, Pawel Moll <pawel.moll@arm.com> wrote:
> In "Device Tree powered" systems, platform devices are usually
> massively populated with of_platform_populate() call, executed
> at some level of initcalls, either by generic architecture
> or by platform-specific code.
>
> There are situations though where certain devices must be
> created (and bound with drivers) before all the others.
> This presents small challenge in DT-driven systems, as
> devices explicitly created in early code would be created
> again by of_platform_populate().
>
> This patch tries to solve that issue in a generic way,
> adding a "populated" flag which is set in the device_node
> structure when a device is being created in the core.
> Later, of_platform_populate() skips such nodes (and
> its children) in a similar way to the non-available ones.
>
> Signed-off-by: Pawel Moll <pawel.moll@arm.com>
> ---
> drivers/base/core.c | 4 ++++
> drivers/of/device.c | 16 ++++++++++++++++
> drivers/of/platform.c | 6 ++++--
> include/linux/of.h | 6 ++++++
> include/linux/of_device.h | 11 +++++++++++
> 5 files changed, 41 insertions(+), 2 deletions(-)
Go read Grant's and my comments from your last posting.
Rob
^ permalink raw reply
* [PATCH] thermal: imx: update formula for thermal sensor
From: Philipp Zabel @ 2014-02-13 19:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140213130252.GB20842@S2101-09.ap.freescale.net>
Hi,
On Thu, Feb 13, 2014 at 2:02 PM, Shawn Guo <shawn.guo@linaro.org> wrote:
>
> Copy LAKML and pengutronix folks in case they have comments.
>
> On Wed, Feb 12, 2014 at 06:06:35PM +0800, Anson Huang wrote:
> > Thermal sensor used to need two calibration points which are
> > in fuse map to get a slope for converting thermal sensor's raw
> > data to real temperature in degree C. Due to the chip calibration
> > limitation, hardware team provides an universal formula to get
> > real temperature from internal thermal sensor raw data:
> >
> > Slope = 0.4297157 - (0.0015976 * 25C fuse);
I have an i.MX6Q 1.2 with 1417 counts at 25?C and 1296 counts at 105?C fused.
Assuming that those values are fused correctly, with the old formula I get:
t1=25, n1=1417, t2=105, n2=1296
c1=1000*(t1-t2)/(n1-n2)=-662, c2=1000*t2-c1*n2=962952
T_meas=c2+c1*N_meas=962952-662*N_meas
N_meas=1417 --> T_meas = 24898
N_meas=1296 --> T_meas = 105000
With the new formula, on the other hand, I would get:
t1=25, n1=1417
F0=10000000, F1=15976, 4297157
c1=F0*1000/(F1*n1-F2)=545, c2=n1*c1+1000*25=797265
T_meas=c2-N_meas*c1=797265-N_meas*545
N_meas=1417 --> T_meas = 25000
N_meas=1296 --> T_meas = 90945
That's off by over 14?C!
The default passive cooling trip point is at 85?C,
which translated to 1326 counts before:
alarm_value=(alarm_temp-c2)/c1=(85000-962952)/(-662)=1326
With the new formula the trip count is only
alarm_value=(c2-alarm_temp)/c1=(797265-85000)/545=1306
That translates to 98.4?C according to the old formula. I think
that is awfully close to the critical trip point at 100?C, which,
according to the old formula, corresponded to 1303 counts.
The new critical trip point for 100?C is at 1279 counts. According
to the old formula this corresponds to 116.2?C, well over the
rated maximum of 105?C.
Do I need to be afraid for my old silicon?
> > Update the formula, as there will be no hot point calibration
> > data in fuse map from now on.
I wonder if it would be better to keep using the high temperature
calibration point if it is fused at all.
regards
Philipp
^ permalink raw reply
* [BISECTED] ssh - Received disconnect from x.x.x.x: 2: Bad packet length 3149594624
From: Ivaylo Dimitrov @ 2014-02-13 18:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140213182105.GN13576@mudshark.cambridge.arm.com>
On 13.02.2014 20:21, Will Deacon wrote:
>
> That's certainly unexpected. The n900 has (iirc) a Cortex-A8, which as an
> ARMv7 core, can perform unaligned accesses to normal, cacheable memory in
> hardware.
>
Yep, Cortex-A8 and it has no problem to do unaligned memory accesses
AFAIK. I suspect it is a driver issue, not CPU.
> Can you provide your .config and/or any information about your network chip
> please? There's a chance that the driver is doing something odd.
The chip is TI wl1251, you can find the config file here(actually this
is the tree I am using to boot 3.14-rc1 on N900) -
https://gitorious.org/linux-n900/freemangordons-linux-n900/source/1434dbd7fbc5ec257b6cd6c547689b79177d1937:arch/arm/configs/rx51_defconfig
Thanks,
Ivo
^ permalink raw reply
* [PATCH] PCI: imx6: Fix link_up detection
From: Marek Vasut @ 2014-02-13 18:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52FD0F1A.4000208@boundarydevices.com>
On Thursday, February 13, 2014 at 07:29:46 PM, Troy Kisky wrote:
> On 2/12/2014 12:36 AM, Marek Vasut wrote:
> > On Wednesday, February 12, 2014 at 08:27:55 AM, Sascha Hauer wrote:
> >
> > +CC Troy Kisky, since I think he submitted something similar some time
> > ago already.
> >
> > Otherwise I agree this happens.
>
> Sorry, I haven't submitted this yet, but was planning to today. Here's
> what would have been sent.
Release early, release often ... ;-)
Best regards,
Marek Vasut
^ permalink raw reply
* [PATCH v3 00/13] pinctrl: mvebu: restructure resource allocation
From: Thomas Petazzoni @ 2014-02-13 18:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52FCFC97.8050200@gmail.com>
Dear Sebastian Hesselbarth,
On Thu, 13 Feb 2014 18:10:47 +0100, Sebastian Hesselbarth wrote:
> >>> I am not sure what you mean here in terms of the ordering for the
> >>> patches. I'm attaching several patches, and the first three patches
> >>> adapt your patch series to also cover 375 and 38x, assuming the pinctrl
> >>> support for 375 and 38x is merged before your patch series.
> >>
> >> Right. If 375/38x pinctrl goes in first (what I expect), I'd have to add
> >> corresponding patches. You already sent them, I'll pick them up.
> >
> > Ok, cool. Hopefully we can sort out the merging of those two patch
> > series for 3.15 with Linus Walleij.
>
> That is the plan - or rather get his Acked-by as we are lucky to have
> pinctrl/mvebu and touching nothing else.
Right.
> > You can take this opportunity to generate:
> >
> > { "mpp0", ... },
> > { "mpp1", ... },
> > { "mpp2", ... },
> > ...
> > { "mpp65", ... },
>
> Ah, ok, I see. Yes that should be doable. We should definitely consider
> this for later, i.e. leave it now as is and rework later.
Sure, as I said, I don't think we should do all the possible
improvements right now. Your patch series is already large enough :-)
That being said, I haven't looked very closely at the Dove pinctrl
driver, and this is the one that does the most funky things, with those
cases where multiple pins are muxed with a single register control.
> > static int armada_370_mpp_ctrl_get(unsigned pid, unsigned long *config)
> > {
> > return default_mpp_ctrl_get(mpp_base, pid, config);
> > }
> >
> > static int armada_370_mpp_ctrl_set(unsigned pid, unsigned long config)
> > {
> > return default_mpp_ctrl_set(mpp_base, pid, config);
> > }
> >
> > but we admittedly cannot completely remove the per-SoC function, since
> > the mpp_base is now only known to each per-SoC driver.
>
> I guess I'll squash the above in for v4.. doesn't look that bad.
Cool, thanks!
Thomas
--
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
^ permalink raw reply
* [PATCH] PCI: imx6: Fix link_up detection
From: Troy Kisky @ 2014-02-13 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201402120836.16221.marex@denx.de>
On 2/12/2014 12:36 AM, Marek Vasut wrote:
> On Wednesday, February 12, 2014 at 08:27:55 AM, Sascha Hauer wrote:
>
> +CC Troy Kisky, since I think he submitted something similar some time ago
> already.
>
> Otherwise I agree this happens.
>
Sorry, I haven't submitted this yet, but was planning to today. Here's
what would have been sent.
From 32c560d33fe2c3945d69f3396689f0abb76f7e1f Mon Sep 17 00:00:00 2001
From: Marek Vasut <marex@denx.de>
Date: Sat, 25 Jan 2014 14:22:48 -0700
Subject: [PATCH 1/1] pci-imx6.c: wait for retraining
This patch handles the case where the PCIe link is up and running, yet drops
into the LTSSM training mode. The link spends short time in the LTSSM training
mode, but the current code can misinterpret it as the link being stalled.
Waiting for the LTSSM training to complete fixes the issue.
Signed-off-by: Marek Vasut <marex@denx.de>
Tested-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
drivers/pci/host/pci-imx6.c | 47 ++++++++++++++++++++++++++++++++-------------
1 file changed, 34 insertions(+), 13 deletions(-)
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index e8663a8..ee08250 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -424,20 +424,40 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp)
static int imx6_pcie_link_up(struct pcie_port *pp)
{
- u32 rc, ltssm, rx_valid;
+ u32 rc, debug_r0, rx_valid;
+ int count = 5;
/*
- * Test if the PHY reports that the link is up and also that
- * the link training finished. It might happen that the PHY
- * reports the link is already up, but the link training bit
- * is still set, so make sure to check the training is done
- * as well here.
+ * Test if the PHY reports that the link is up and also that the LTSSM
+ * training finished. There are three possible states of the link when
+ * this code is called:
+ * 1) The link is DOWN (unlikely)
+ * The link didn't come up yet for some reason. This usually means
+ * we have a real problem somewhere. Reset the PHY and exit. This
+ * state calls for inspection of the DEBUG registers.
+ * 2) The link is UP, but still in LTSSM training
+ * Wait for the training to finish, which should take a very short
+ * time. If the training does not finish, we have a problem and we
+ * need to inspect the DEBUG registers. If the training does finish,
+ * the link is up and operating correctly.
+ * 3) The link is UP and no longer in LTSSM training
+ * The link is up and operating correctly.
*/
- rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
- if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) &&
- !(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING))
- return 1;
-
+ while (1) {
+ rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
+ if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP))
+ break;
+ if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING))
+ return 1;
+ if (!count--)
+ break;
+ dev_dbg(pp->dev, "Link is up, but still in training\n");
+ /*
+ * Wait a little bit, then re-check if the link finished
+ * the training.
+ */
+ usleep_range(1000, 2000);
+ }
/*
* From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
* Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
@@ -446,15 +466,16 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
* to gen2 is stuck
*/
pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
- ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F;
+ debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
if (rx_valid & 0x01)
return 0;
- if (ltssm != 0x0d)
+ if ((debug_r0 & 0x3f) != 0x0d)
return 0;
dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n");
+ dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc);
imx6_pcie_reset_phy(pp);
--
1.8.1.2
^ permalink raw reply related
* [PATCH v2 0/3] ARM: PCI: implement generic PCI host controller
From: Jason Gunthorpe @ 2014-02-13 18:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392236171-10512-1-git-send-email-will.deacon@arm.com>
On Wed, Feb 12, 2014 at 08:16:08PM +0000, Will Deacon wrote:
> Like v1, I continue to support only a single controller and therefore a
> single I/O space. I'm not sure how to represent multiple controllers in the
> device-tree without inventing some horrible hacks, so any ideas in this area
> would be much appreciated.
The DT representation is very straightforward, just have more copies
of what you already have. Each DT stanza should be represented in
Linux a distinct PCI domain.
In Linux you run into two small problems
1) PCI Domain numbers needs to be allocated dynamically
* I think there should be a core thing to allocate a domain
object w/ a struct device, and assign a unique domain number.
We are already seeing drivers do things like keep track
of their own domain numbers via a counter (pcie-designware.c)
The host bridge object is similar to this but it isn't focused
on a domain.
2) The space in the IO fixed mapping needs to be allocated to PCI
host drivers dynamically
* pci_ioremap_io_dynamic that takes a bus address + cpu_physical
address and returns a Linux virtual address.
The first caller can get a nice traslation where bus address ==
Linux virtual address, everyone after can get best efforts.
You will have overlapping physical IO bus addresses - each domain will
have a 0 IO BAR - but those will have distinct CPU physical addresses
and can then be uniquely mapped into the IO mapping. So at the struct
resource level the two domains have disjoint IO addresses, but each
domain uses a different IO offset..
Jason
^ permalink raw reply
* [PATCH v2 3/3] PCI: ARM: add support for generic PCI host controller
From: Jason Gunthorpe @ 2014-02-13 18:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4110788.UI9TADVhpa@wuerfel>
On Thu, Feb 13, 2014 at 05:28:20PM +0100, Arnd Bergmann wrote:
> > Huh? The reg property clearly has the size in it (as shown in the
> > example below). I guess I was just asking for the description
> > here to say what the size was for the 2 compatibles since its
> > fixed and known.
>
> It's still an open question whether the config space in the reg
> property should cover all 256 buses or just the ones in the
> bus-range. In the latter case, it would be variable (but
> predictable) size.
The 'describe the hardware principle' says the reg should be the
entire available ECAM/CAM region the hardware is able to support.
This may be less than 256 busses, as ECAM allows the implementor to
select how many upper address bits are actually supported.
IMHO, the bus-range should be used to indicate the range of busses
discovered by the firmware, but we have historically tweaked it to
indicate the max range of bus numbers available on this bus (I think
to support the hack where two physical PCI domains were roughly glued
into a single Linux domain).
Which is not necessary when the DT stanza maps 1:1 into a PCI
domain. The driver default for bus-range should just be 0 to 255, and
it shouldn't be included in most cases.
The max busnr resource passed to the PCI core should be the lower of
the busnr property and the reg limit, IMHO.
The issue with burning VM in Linux is a Linux issue and shouldn't leak
into the DT... Ideally the solution here would be to have the PCI core
call back to the host driver when it allocates/deallocates a bus
number and then the driver can manage the config space VM mapping on a
bus by bus basis.
Jason
^ permalink raw reply
* [PATCH 4/4] ARM: dts: OMAP5: Add IOMMU nodes
From: Suman Anna @ 2014-02-13 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315776-33197-1-git-send-email-s-anna@ti.com>
The IOMMU DT nodes have been added for the DSP and IPU
subsystems. The MMUs in OMAP5 are identical to those in
OMAP4, including the bus error back capability on IPU.
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/boot/dts/omap5.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index a72813a..a78fdaa 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -513,6 +513,23 @@
dma-names = "tx", "rx";
};
+ mmu_dsp: mmu at 4a066000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x4a066000 0xff>;
+ interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_dsp";
+ dma-window = <0x20000000 0xdffff000>;
+ };
+
+ mmu_ipu: mmu at 55082000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x55082000 0xff>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_ipu";
+ dma-window = <0 0xdffff000>;
+ ti,iommu-bus-err-back;
+ };
+
keypad: keypad at 4ae1c000 {
compatible = "ti,omap4-keypad";
reg = <0x4ae1c000 0x400>;
--
1.8.5.3
^ permalink raw reply related
* [PATCH 3/4] ARM: dts: OMAP4: Add IOMMU nodes
From: Suman Anna @ 2014-02-13 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315776-33197-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
Add the IOMMU nodes for the DSP and IPU subsystems. The external
address space for DSP starts at 0x20000000 in OMAP4 compared to
0x11000000 in OMAP3, and the addresses beyond 0xE0000000 are
private address space for the Cortex-M3 cores in the IPU subsystem.
The MMU within the IPU sub-system also supports a bus error back
capability, not available on the DSP MMU.
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
[s-anna at ti.com: dma-window updates and bus error back addition]
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/boot/dts/omap4.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index d3f8a6e..1885f90 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -461,6 +461,23 @@
dma-names = "tx", "rx";
};
+ mmu_dsp: mmu at 4a066000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x4a066000 0xff>;
+ interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_dsp";
+ dma-window = <0x20000000 0xdffff000>;
+ };
+
+ mmu_ipu: mmu at 55082000 {
+ compatible = "ti,omap4-iommu";
+ reg = <0x55082000 0xff>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mmu_ipu";
+ dma-window = <0 0xdffff000>;
+ ti,iommu-bus-err-back;
+ };
+
wdt2: wdt at 4a314000 {
compatible = "ti,omap4-wdt", "ti,omap3-wdt";
reg = <0x4a314000 0x80>;
--
1.8.5.3
^ permalink raw reply related
* [PATCH 2/4] ARM: dts: OMAP3: Add IVA IOMMU node
From: Suman Anna @ 2014-02-13 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315776-33197-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
Add the DT node for the IOMMU within the DSP subsystem. The entry
is disabled to keep in line with the current hwmod usage.
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
[s-anna at ti.com: split the entry and disable the node]
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/boot/dts/omap3.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index ac91cc3..9607187 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -419,6 +419,15 @@
dma-window = <0 0xfffff000>;
};
+ mmu_iva: mmu at 5d000000 {
+ compatible = "ti,omap2-iommu";
+ reg = <0x5d000000 0x80>;
+ interrupts = <28>;
+ ti,hwmods = "mmu_iva";
+ dma-window = <0x11000000 0xeefff000>;
+ status = "disabled";
+ };
+
wdt2: wdt at 48314000 {
compatible = "ti,omap3-wdt";
reg = <0x48314000 0x80>;
--
1.8.5.3
^ permalink raw reply related
* [PATCH 1/4] ARM: dts: OMAP3: Update ISP IOMMU node
From: Suman Anna @ 2014-02-13 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315776-33197-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
Update the IOMMU node for the camera subsystem as per the
OMAP IOMMU bindings.
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
[s-anna at ti.com: corrected interrupt number]
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/boot/dts/omap3.dtsi | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index a5fc83b..ac91cc3 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -411,10 +411,12 @@
};
mmu_isp: mmu at 480bd400 {
- compatible = "ti,omap3-mmu-isp";
- ti,hwmods = "mmu_isp";
+ compatible = "ti,omap2-iommu";
reg = <0x480bd400 0x80>;
- interrupts = <8>;
+ interrupts = <24>;
+ ti,hwmods = "mmu_isp";
+ ti,#tlb-entries = <8>;
+ dma-window = <0 0xfffff000>;
};
wdt2: wdt at 48314000 {
--
1.8.5.3
^ permalink raw reply related
* [PATCH 0/4] OMAP IOMMU DTS nodes
From: Suman Anna @ 2014-02-13 18:22 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
This series includes patches that adds the iommu DT nodes on
OMAP3 (IVA), and OMAP4 and OMAP5 SoCs. It also includes an
updated OMAP3 ISP iommu DT node patch posted previously [1].
Posting the series separately from the driver DT adapation
changes [2]. The series adds the DTS patches in line with the
new OMAP IOMMU bindings [3] posted alongside the DT adaptation.
[1] http://marc.info/?l=linux-omap&m=138728486000628&w=2
[2] http://marc.info/?l=linux-omap&m=139231544416973&w=2
[3] http://marc.info/?l=linux-omap&m=139231545116981&w=2
Florian Vaussard (3):
ARM: dts: OMAP3: Update ISP IOMMU node
ARM: dts: OMAP3: Add IVA IOMMU node
ARM: dts: OMAP4: Add IOMMU nodes
Suman Anna (1):
ARM: dts: OMAP5: Add IOMMU nodes
arch/arm/boot/dts/omap3.dtsi | 17 ++++++++++++++---
arch/arm/boot/dts/omap4.dtsi | 17 +++++++++++++++++
arch/arm/boot/dts/omap5.dtsi | 17 +++++++++++++++++
3 files changed, 48 insertions(+), 3 deletions(-)
--
1.8.5.3
^ permalink raw reply
* [BISECTED] ssh - Received disconnect from x.x.x.x: 2: Bad packet length 3149594624
From: Will Deacon @ 2014-02-13 18:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52FD08D1.3030405@gmail.com>
Hi Ivo,
Thanks for bisecting this.
On Thu, Feb 13, 2014 at 06:02:57PM +0000, Ivaylo Dimitrov wrote:
> Since next-21012014 I started to receive "Received disconnect from
> x.x.x.x: 2: Bad packet length 3149594624" errors when trying to
> establish ssh session over wifi to my N900, running Maemo 5. I bisected
> and it turned out that commit dce5c9e35bc4085bd33eccdb8c9ec5a643507a14
> "ARM: 7928/1: kconfig: select HAVE_EFFICIENT_UNALIGNED_ACCESS for CPUv6+
> && MMU" is the first bad. There is nothing related in either dmesg or
> syslog on the device. Reverting that commit on 3.14-rc1 make those
> errors disappear.
That's certainly unexpected. The n900 has (iirc) a Cortex-A8, which as an
ARMv7 core, can perform unaligned accesses to normal, cacheable memory in
hardware.
Can you provide your .config and/or any information about your network chip
please? There's a chance that the driver is doing something odd.
Cheers,
Will
^ permalink raw reply
* [PATCHv2 16/16] ARM: OMAP2+: Remove legacy omap-iommu.c
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
With full DT boot, the legacy mode of platform device creation
for OMAP IOMMUs is not needed anymore.
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
---
arch/arm/mach-omap2/Makefile | 3 --
arch/arm/mach-omap2/omap-iommu.c | 79 ----------------------------------------
2 files changed, 82 deletions(-)
delete mode 100644 arch/arm/mach-omap2/omap-iommu.c
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index e6eec6f..242933f 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -226,9 +226,6 @@ obj-$(CONFIG_SOC_DRA7XX) += omap_hwmod_7xx_data.o
obj-$(CONFIG_OMAP3_EMU) += emu.o
obj-$(CONFIG_HW_PERF_EVENTS) += pmu.o
-iommu-$(CONFIG_OMAP_IOMMU) := omap-iommu.o
-obj-y += $(iommu-m) $(iommu-y)
-
ifneq ($(CONFIG_TIDSPBRIDGE),)
obj-y += dsp.o
endif
diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c
deleted file mode 100644
index f1fab56..0000000
--- a/arch/arm/mach-omap2/omap-iommu.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * omap iommu: omap device registration
- *
- * Copyright (C) 2008-2009 Nokia Corporation
- *
- * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/of.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-
-#include <linux/platform_data/iommu-omap.h>
-#include "soc.h"
-#include "omap_hwmod.h"
-#include "omap_device.h"
-
-static int __init omap_iommu_dev_init(struct omap_hwmod *oh, void *unused)
-{
- struct platform_device *pdev;
- struct iommu_platform_data *pdata;
- struct omap_mmu_dev_attr *a = (struct omap_mmu_dev_attr *)oh->dev_attr;
- static int i;
-
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- pdata->name = oh->name;
- pdata->nr_tlb_entries = a->nr_tlb_entries;
- pdata->da_start = a->da_start;
- pdata->da_end = a->da_end;
-
- if (oh->rst_lines_cnt == 1) {
- pdata->reset_name = oh->rst_lines->name;
- pdata->assert_reset = omap_device_assert_hardreset;
- pdata->deassert_reset = omap_device_deassert_hardreset;
- }
-
- pdev = omap_device_build("omap-iommu", i, oh, pdata, sizeof(*pdata));
-
- kfree(pdata);
-
- if (IS_ERR(pdev)) {
- pr_err("%s: device build err: %ld\n", __func__, PTR_ERR(pdev));
- return PTR_ERR(pdev);
- }
-
- i++;
-
- return 0;
-}
-
-static int __init omap_iommu_init(void)
-{
- /* If dtb is there, the devices will be created dynamically */
- if (of_have_populated_dt())
- return -ENODEV;
-
- return omap_hwmod_for_each_by_class("mmu", omap_iommu_dev_init, NULL);
-}
-/* must be ready before omap3isp is probed */
-omap_subsys_initcall(omap_iommu_init);
-
-static void __exit omap_iommu_exit(void)
-{
- /* Do nothing */
-}
-module_exit(omap_iommu_exit);
-
-MODULE_AUTHOR("Hiroshi DOYU");
-MODULE_DESCRIPTION("omap iommu: omap device registration");
-MODULE_LICENSE("GPL v2");
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 15/16] ARM: OMAP4: hwmod data: cleanup data for IOMMUs
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
The device attribute data and ocp address space have all been
cleaned up for OMAP4 iommus. All this data is populated via
the corresponding dt node.
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 35 ------------------------------
1 file changed, 35 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 3318cae9..caca6c2 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -29,7 +29,6 @@
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
-#include <linux/platform_data/iommu-omap.h>
#include <plat/dmtimer.h>
#include "omap_hwmod.h"
@@ -2083,32 +2082,16 @@ static struct omap_hwmod_class omap44xx_mmu_hwmod_class = {
/* mmu ipu */
-static struct omap_mmu_dev_attr mmu_ipu_dev_attr = {
- .da_start = 0x0,
- .da_end = 0xfffff000,
- .nr_tlb_entries = 32,
-};
-
static struct omap_hwmod omap44xx_mmu_ipu_hwmod;
static struct omap_hwmod_rst_info omap44xx_mmu_ipu_resets[] = {
{ .name = "mmu_cache", .rst_shift = 2 },
};
-static struct omap_hwmod_addr_space omap44xx_mmu_ipu_addrs[] = {
- {
- .pa_start = 0x55082000,
- .pa_end = 0x550820ff,
- .flags = ADDR_TYPE_RT,
- },
- { }
-};
-
/* l3_main_2 -> mmu_ipu */
static struct omap_hwmod_ocp_if omap44xx_l3_main_2__mmu_ipu = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_mmu_ipu_hwmod,
.clk = "l3_div_ck",
- .addr = omap44xx_mmu_ipu_addrs,
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
@@ -2127,37 +2110,20 @@ static struct omap_hwmod omap44xx_mmu_ipu_hwmod = {
.modulemode = MODULEMODE_HWCTRL,
},
},
- .dev_attr = &mmu_ipu_dev_attr,
};
/* mmu dsp */
-static struct omap_mmu_dev_attr mmu_dsp_dev_attr = {
- .da_start = 0x0,
- .da_end = 0xfffff000,
- .nr_tlb_entries = 32,
-};
-
static struct omap_hwmod omap44xx_mmu_dsp_hwmod;
static struct omap_hwmod_rst_info omap44xx_mmu_dsp_resets[] = {
{ .name = "mmu_cache", .rst_shift = 1 },
};
-static struct omap_hwmod_addr_space omap44xx_mmu_dsp_addrs[] = {
- {
- .pa_start = 0x4a066000,
- .pa_end = 0x4a0660ff,
- .flags = ADDR_TYPE_RT,
- },
- { }
-};
-
/* l4_cfg -> dsp */
static struct omap_hwmod_ocp_if omap44xx_l4_cfg__mmu_dsp = {
.master = &omap44xx_l4_cfg_hwmod,
.slave = &omap44xx_mmu_dsp_hwmod,
.clk = "l4_div_ck",
- .addr = omap44xx_mmu_dsp_addrs,
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
@@ -2176,7 +2142,6 @@ static struct omap_hwmod omap44xx_mmu_dsp_hwmod = {
.modulemode = MODULEMODE_HWCTRL,
},
},
- .dev_attr = &mmu_dsp_dev_attr,
};
/*
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 14/16] ARM: OMAP3: hwmod data: cleanup data for IOMMUs
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
The irq numbers, ocp address space and device attribute data
have all been cleaned up for OMAP3 IOMMUs. All this data is
populated via the corresponding dt node.
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 46 ------------------------------
1 file changed, 46 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 9c7e23a..d68c131 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -24,7 +24,6 @@
#include "l4_3xxx.h"
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
-#include <linux/platform_data/iommu-omap.h>
#include <linux/platform_data/mailbox-omap.h>
#include <plat/dmtimer.h>
@@ -2991,83 +2990,39 @@ static struct omap_hwmod_class omap3xxx_mmu_hwmod_class = {
/* mmu isp */
-static struct omap_mmu_dev_attr mmu_isp_dev_attr = {
- .da_start = 0x0,
- .da_end = 0xfffff000,
- .nr_tlb_entries = 8,
-};
-
static struct omap_hwmod omap3xxx_mmu_isp_hwmod;
-static struct omap_hwmod_irq_info omap3xxx_mmu_isp_irqs[] = {
- { .irq = 24 + OMAP_INTC_START, },
- { .irq = -1 }
-};
-
-static struct omap_hwmod_addr_space omap3xxx_mmu_isp_addrs[] = {
- {
- .pa_start = 0x480bd400,
- .pa_end = 0x480bd47f,
- .flags = ADDR_TYPE_RT,
- },
- { }
-};
/* l4_core -> mmu isp */
static struct omap_hwmod_ocp_if omap3xxx_l4_core__mmu_isp = {
.master = &omap3xxx_l4_core_hwmod,
.slave = &omap3xxx_mmu_isp_hwmod,
- .addr = omap3xxx_mmu_isp_addrs,
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
static struct omap_hwmod omap3xxx_mmu_isp_hwmod = {
.name = "mmu_isp",
.class = &omap3xxx_mmu_hwmod_class,
- .mpu_irqs = omap3xxx_mmu_isp_irqs,
.main_clk = "cam_ick",
- .dev_attr = &mmu_isp_dev_attr,
.flags = HWMOD_NO_IDLEST,
};
/* mmu iva */
-static struct omap_mmu_dev_attr mmu_iva_dev_attr = {
- .da_start = 0x11000000,
- .da_end = 0xfffff000,
- .nr_tlb_entries = 32,
-};
-
static struct omap_hwmod omap3xxx_mmu_iva_hwmod;
-static struct omap_hwmod_irq_info omap3xxx_mmu_iva_irqs[] = {
- { .irq = 28 + OMAP_INTC_START, },
- { .irq = -1 }
-};
-
static struct omap_hwmod_rst_info omap3xxx_mmu_iva_resets[] = {
{ .name = "mmu", .rst_shift = 1, .st_shift = 9 },
};
-static struct omap_hwmod_addr_space omap3xxx_mmu_iva_addrs[] = {
- {
- .pa_start = 0x5d000000,
- .pa_end = 0x5d00007f,
- .flags = ADDR_TYPE_RT,
- },
- { }
-};
-
/* l3_main -> iva mmu */
static struct omap_hwmod_ocp_if omap3xxx_l3_main__mmu_iva = {
.master = &omap3xxx_l3_main_hwmod,
.slave = &omap3xxx_mmu_iva_hwmod,
- .addr = omap3xxx_mmu_iva_addrs,
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
static struct omap_hwmod omap3xxx_mmu_iva_hwmod = {
.name = "mmu_iva",
.class = &omap3xxx_mmu_hwmod_class,
- .mpu_irqs = omap3xxx_mmu_iva_irqs,
.clkdm_name = "iva2_clkdm",
.rst_lines = omap3xxx_mmu_iva_resets,
.rst_lines_cnt = ARRAY_SIZE(omap3xxx_mmu_iva_resets),
@@ -3080,7 +3035,6 @@ static struct omap_hwmod omap3xxx_mmu_iva_hwmod = {
.idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT,
},
},
- .dev_attr = &mmu_iva_dev_attr,
.flags = HWMOD_NO_IDLEST,
};
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 13/16] ARM: OMAP2+: extend iommu pdata-quirks to OMAP5
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
OMAP5 has the same iommus as OMAP4, so extend the OMAP4
iommu pdata quirks for OMAP5 as well.
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/pdata-quirks.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index 74e094a..551877f 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -193,7 +193,9 @@ static void __init omap4_panda_legacy_init(void)
legacy_init_ehci_clk("auxclk3_ck");
legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 53);
}
+#endif
+#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
static struct iommu_platform_data omap4_iommu_pdata = {
.reset_name = "mmu_cache",
.assert_reset = omap_device_assert_hardreset,
@@ -264,6 +266,8 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
#ifdef CONFIG_ARCH_OMAP4
OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
+#endif
+#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
OF_DEV_AUXDATA("ti,omap4-iommu", 0x4a066000, "4a066000.mmu",
&omap4_iommu_pdata),
OF_DEV_AUXDATA("ti,omap4-iommu", 0x55082000, "55082000.mmu",
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 12/16] ARM: OMAP5: hwmod data: add mmu data for ipu & dsp
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
A new MMU hwmod class and data structures are created
to represent the MMUs within the IPU and DSP processor
subsystems in OMAP5. The MMUs in OMAP5 are identical to
those in OMAP4.
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_54xx_data.c | 83 ++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
diff --git a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
index e297d62..8923172 100644
--- a/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_54xx_data.c
@@ -1122,6 +1122,71 @@ static struct omap_hwmod omap54xx_mmc5_hwmod = {
};
/*
+ * 'mmu' class
+ * The memory management unit performs virtual to physical address translation
+ * for its requestors.
+ */
+
+static struct omap_hwmod_class_sysconfig omap54xx_mmu_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET |
+ SYSS_HAS_RESET_STATUS),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
+static struct omap_hwmod_class omap54xx_mmu_hwmod_class = {
+ .name = "mmu",
+ .sysc = &omap54xx_mmu_sysc,
+};
+
+static struct omap_hwmod_rst_info omap54xx_mmu_dsp_resets[] = {
+ { .name = "mmu_cache", .rst_shift = 1 },
+};
+
+static struct omap_hwmod omap54xx_mmu_dsp_hwmod = {
+ .name = "mmu_dsp",
+ .class = &omap54xx_mmu_hwmod_class,
+ .clkdm_name = "dsp_clkdm",
+ .rst_lines = omap54xx_mmu_dsp_resets,
+ .rst_lines_cnt = ARRAY_SIZE(omap54xx_mmu_dsp_resets),
+ .main_clk = "dpll_iva_h11x2_ck",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = OMAP54XX_CM_DSP_DSP_CLKCTRL_OFFSET,
+ .rstctrl_offs = OMAP54XX_RM_DSP_RSTCTRL_OFFSET,
+ .context_offs = OMAP54XX_RM_DSP_DSP_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_HWCTRL,
+ },
+ },
+};
+
+/* mmu ipu */
+static struct omap_hwmod_rst_info omap54xx_mmu_ipu_resets[] = {
+ { .name = "mmu_cache", .rst_shift = 2 },
+};
+
+static struct omap_hwmod omap54xx_mmu_ipu_hwmod = {
+ .name = "mmu_ipu",
+ .class = &omap54xx_mmu_hwmod_class,
+ .clkdm_name = "ipu_clkdm",
+ .rst_lines = omap54xx_mmu_ipu_resets,
+ .rst_lines_cnt = ARRAY_SIZE(omap54xx_mmu_ipu_resets),
+ .main_clk = "dpll_core_h22x2_ck",
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = OMAP54XX_CM_IPU_IPU_CLKCTRL_OFFSET,
+ .rstctrl_offs = OMAP54XX_RM_IPU_RSTCTRL_OFFSET,
+ .context_offs = OMAP54XX_RM_IPU_IPU_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_HWCTRL,
+ },
+ },
+};
+
+/*
* 'mpu' class
* mpu sub-system
*/
@@ -1763,6 +1828,14 @@ static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_1 = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
+/* l4_cfg -> mmu_dsp */
+static struct omap_hwmod_ocp_if omap54xx_l4_cfg__mmu_dsp = {
+ .master = &omap54xx_l4_cfg_hwmod,
+ .slave = &omap54xx_mmu_dsp_hwmod,
+ .clk = "l4_root_clk_div",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
/* mpu -> l3_main_1 */
static struct omap_hwmod_ocp_if omap54xx_mpu__l3_main_1 = {
.master = &omap54xx_mpu_hwmod,
@@ -1787,6 +1860,14 @@ static struct omap_hwmod_ocp_if omap54xx_l4_cfg__l3_main_2 = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
+/* l3_main_2 -> mmu_ipu */
+static struct omap_hwmod_ocp_if omap54xx_l3_main_2__mmu_ipu = {
+ .master = &omap54xx_l3_main_2_hwmod,
+ .slave = &omap54xx_mmu_ipu_hwmod,
+ .clk = "l3_iclk_div",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
/* l3_main_1 -> l3_main_3 */
static struct omap_hwmod_ocp_if omap54xx_l3_main_1__l3_main_3 = {
.master = &omap54xx_l3_main_1_hwmod,
@@ -2345,6 +2426,7 @@ static struct omap_hwmod_ocp_if *omap54xx_hwmod_ocp_ifs[] __initdata = {
&omap54xx_l4_wkup__counter_32k,
&omap54xx_l4_cfg__dma_system,
&omap54xx_l4_abe__dmic,
+ &omap54xx_l4_cfg__mmu_dsp,
&omap54xx_mpu__emif1,
&omap54xx_mpu__emif2,
&omap54xx_l4_wkup__gpio1,
@@ -2360,6 +2442,7 @@ static struct omap_hwmod_ocp_if *omap54xx_hwmod_ocp_ifs[] __initdata = {
&omap54xx_l4_per__i2c3,
&omap54xx_l4_per__i2c4,
&omap54xx_l4_per__i2c5,
+ &omap54xx_l3_main_2__mmu_ipu,
&omap54xx_l4_wkup__kbd,
&omap54xx_l4_cfg__mailbox,
&omap54xx_l4_abe__mcbsp1,
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 11/16] ARM: OMAP3: fix iva mmu programming issues
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
The IVA MMU is not functional when used through the hwmod and
omap_device layers. Add fixes to clockdomain and hwmod data
to have it functional. The hwmod changes are needed to enable
the clock, and the SWSUP change is needed to wakeup the domain
because the power domain is programmed to be in RET, and there
is no automatic power domain switching to ON.
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/clockdomains3xxx_data.c | 2 +-
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-omap2/clockdomains3xxx_data.c b/arch/arm/mach-omap2/clockdomains3xxx_data.c
index e6b91e5..f03dc97 100644
--- a/arch/arm/mach-omap2/clockdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/clockdomains3xxx_data.c
@@ -247,7 +247,7 @@ static struct clockdomain neon_clkdm = {
static struct clockdomain iva2_clkdm = {
.name = "iva2_clkdm",
.pwrdm = { .name = "iva2_pwrdm" },
- .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .flags = CLKDM_CAN_SWSUP,
.dep_bit = OMAP3430_PM_WKDEP_MPU_EN_IVA2_SHIFT,
.wkdep_srcs = iva2_wkdeps,
.clktrctrl_mask = OMAP3430_CLKTRCTRL_IVA2_MASK,
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 81dd071..9c7e23a 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -3068,12 +3068,16 @@ static struct omap_hwmod omap3xxx_mmu_iva_hwmod = {
.name = "mmu_iva",
.class = &omap3xxx_mmu_hwmod_class,
.mpu_irqs = omap3xxx_mmu_iva_irqs,
+ .clkdm_name = "iva2_clkdm",
.rst_lines = omap3xxx_mmu_iva_resets,
.rst_lines_cnt = ARRAY_SIZE(omap3xxx_mmu_iva_resets),
.main_clk = "iva2_ck",
.prcm = {
.omap2 = {
.module_offs = OMAP3430_IVA2_MOD,
+ .module_bit = OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_SHIFT,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT,
},
},
.dev_attr = &mmu_iva_dev_attr,
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 10/16] ARM: OMAP2+: use pdata quirks for iommu reset lines
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
The OMAP iommu driver performs the reset management for the
iommu instances in processor sub-systems using the omap_device
API which are currently supplied as platform data ops. Use pdata
quirks to maintain the functionality as the OMAP iommu driver
gets converted to use DT nodes, until the reset portions are
decoupled from omap_hwmod/omap_device into a separate reset
driver.
This patch adds the pdata quirks for the reset management of
iommus within the DSP (OMAP3 & OMAP4) and IPU subsystems (OMAP4).
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/pdata-quirks.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index 3d5b24d..74e094a 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -16,12 +16,14 @@
#include <linux/wl12xx.h>
#include <linux/platform_data/pinctrl-single.h>
+#include <linux/platform_data/iommu-omap.h>
#include "am35xx.h"
#include "common.h"
#include "common-board-devices.h"
#include "dss-common.h"
#include "control.h"
+#include "omap_device.h"
struct pdata_init {
const char *compatible;
@@ -92,6 +94,12 @@ static void __init hsmmc2_internal_input_clk(void)
omap_ctrl_writel(reg, OMAP343X_CONTROL_DEVCONF1);
}
+static struct iommu_platform_data omap3_iommu_pdata = {
+ .reset_name = "mmu",
+ .assert_reset = omap_device_assert_hardreset,
+ .deassert_reset = omap_device_deassert_hardreset,
+};
+
static int omap3_sbc_t3730_twl_callback(struct device *dev,
unsigned gpio,
unsigned ngpio)
@@ -185,6 +193,12 @@ static void __init omap4_panda_legacy_init(void)
legacy_init_ehci_clk("auxclk3_ck");
legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 53);
}
+
+static struct iommu_platform_data omap4_iommu_pdata = {
+ .reset_name = "mmu_cache",
+ .assert_reset = omap_device_assert_hardreset,
+ .deassert_reset = omap_device_deassert_hardreset,
+};
#endif
#ifdef CONFIG_SOC_OMAP5
@@ -240,6 +254,8 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
#ifdef CONFIG_ARCH_OMAP3
OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002030, "48002030.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002a00, "48002a00.pinmux", &pcs_pdata),
+ OF_DEV_AUXDATA("ti,omap2-iommu", 0x5d000000, "5d000000.mmu",
+ &omap3_iommu_pdata),
/* Only on am3517 */
OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL),
OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
@@ -248,6 +264,10 @@ struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
#ifdef CONFIG_ARCH_OMAP4
OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a100040, "4a100040.pinmux", &pcs_pdata),
OF_DEV_AUXDATA("ti,omap4-padconf", 0x4a31e040, "4a31e040.pinmux", &pcs_pdata),
+ OF_DEV_AUXDATA("ti,omap4-iommu", 0x4a066000, "4a066000.mmu",
+ &omap4_iommu_pdata),
+ OF_DEV_AUXDATA("ti,omap4-iommu", 0x55082000, "55082000.mmu",
+ &omap4_iommu_pdata),
#endif
{ /* sentinel */ },
};
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 09/16] ARM: OMAP2+: change the ISP device archdata MMU name
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
The IOMMU DT adaptation support uses the device name instead
of an iommu object name. The iommu object names should eventually
vanish when all the IOMMU users have been converted to DT nodes.
NOTE: This change is not compatible with legacy boots, but OMAP3
is expected to be DT-boot only going forward.
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/devices.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 0dd6398..3bf0452 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -224,7 +224,7 @@ static struct platform_device omap3isp_device = {
};
static struct omap_iommu_arch_data omap3_isp_iommu = {
- .name = "mmu_isp",
+ .name = "480bd400.mmu",
};
int omap3_init_camera(struct isp_platform_data *pdata)
--
1.8.5.3
^ permalink raw reply related
* [PATCHv2 08/16] ARM: OMAP3: remove deprecated CONFIG_OMAP_IOMMU_IVA2
From: Suman Anna @ 2014-02-13 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1392315347-32967-1-git-send-email-s-anna@ti.com>
From: Florian Vaussard <florian.vaussard@epfl.ch>
CONFIG_OMAP_IOMMU_IVA2 was defined originally to avoid conflicting
usage by tidspbridge and other iommu users. The same can be achieved
by marking the DT node disabled, so remove this obsolete flag and
the corresponding hwmod data can be enabled.
Cc: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch>
[s-anna at ti.com: revise commit log]
Signed-off-by: Suman Anna <s-anna@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 8 --------
arch/arm/plat-omap/Kconfig | 3 ---
2 files changed, 11 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 4c3b1e6..81dd071 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -3029,8 +3029,6 @@ static struct omap_hwmod omap3xxx_mmu_isp_hwmod = {
.flags = HWMOD_NO_IDLEST,
};
-#ifdef CONFIG_OMAP_IOMMU_IVA2
-
/* mmu iva */
static struct omap_mmu_dev_attr mmu_iva_dev_attr = {
@@ -3082,8 +3080,6 @@ static struct omap_hwmod omap3xxx_mmu_iva_hwmod = {
.flags = HWMOD_NO_IDLEST,
};
-#endif
-
/* l4_per -> gpio4 */
static struct omap_hwmod_addr_space omap3xxx_gpio4_addrs[] = {
{
@@ -3855,9 +3851,7 @@ static struct omap_hwmod_ocp_if *omap34xx_hwmod_ocp_ifs[] __initdata = {
&omap3xxx_l4_core__hdq1w,
&omap3xxx_sad2d__l3,
&omap3xxx_l4_core__mmu_isp,
-#ifdef CONFIG_OMAP_IOMMU_IVA2
&omap3xxx_l3_main__mmu_iva,
-#endif
&omap34xx_l4_core__ssi,
NULL
};
@@ -3881,9 +3875,7 @@ static struct omap_hwmod_ocp_if *omap36xx_hwmod_ocp_ifs[] __initdata = {
&omap3xxx_l4_core__hdq1w,
&omap3xxx_sad2d__l3,
&omap3xxx_l4_core__mmu_isp,
-#ifdef CONFIG_OMAP_IOMMU_IVA2
&omap3xxx_l3_main__mmu_iva,
-#endif
NULL
};
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 436ea97..02fc10d 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -86,9 +86,6 @@ config OMAP_MUX_WARNINGS
to change the pin multiplexing setup. When there are no warnings
printed, it's safe to deselect OMAP_MUX for your product.
-config OMAP_IOMMU_IVA2
- bool
-
config OMAP_MPU_TIMER
bool "Use mpu timer"
depends on ARCH_OMAP1
--
1.8.5.3
^ 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