* Re: [PATCH V4 1/9] PM / OPP: Allow OPP table to be used for power-domains
From: Sudeep Holla @ 2017-04-13 13:42 UTC (permalink / raw)
To: Viresh Kumar
Cc: Sudeep Holla, Rafael Wysocki, ulf.hansson, Kevin Hilman,
Viresh Kumar, Nishanth Menon, Stephen Boyd, linaro-kernel,
linux-pm, linux-kernel, Vincent Guittot, robh+dt, lina.iyer,
rnayak, devicetree
In-Reply-To: <20170413053736.GM5910@vireshk-i7>
On 13/04/17 06:37, Viresh Kumar wrote:
> On 12-04-17, 17:49, Sudeep Holla wrote:
>> On 20/03/17 09:32, Viresh Kumar wrote:
>>> diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
>>> index 63725498bd20..d0b95c9e1011 100644
>>> --- a/Documentation/devicetree/bindings/opp/opp.txt
>>> +++ b/Documentation/devicetree/bindings/opp/opp.txt
>>> @@ -76,10 +76,9 @@ This describes the OPPs belonging to a device. This node can have following
>>> This defines voltage-current-frequency combinations along with other related
>>> properties.
>>>
>>> -Required properties:
>>> +Optional properties:
>>> - opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer.
>>>
>>> -Optional properties:
>>> - opp-microvolt: voltage in micro Volts.
>>>
>>> A single regulator's voltage is specified with an array of size one or three.
>>> @@ -154,6 +153,19 @@ properties.
>>>
>>> - status: Marks the node enabled/disabled.
>>>
>>> +- domain-performance-state: A positive integer value representing the minimum
>>> + power-domain performance level required by the device for the OPP node. The
>>
>> So the above definition is when this field in in the device node rather
>> than the OPP table entry, right ?
>
> No. We are updating the opp.txt file here and so it is not about the
> device node. The OPP node entries will contain this field for two
> cases:
> - The OPP table belongs to a power domain
> - The OPP table belongs to a device whose power domain supports
> performance-states.
>
Understood.
>> For simplicity why not have the
>> properties named slightly different or just use phandle to an entry in
>> the device node for this purpose.
>
> We really need a value here. For example, in case where the OPP table
> defines the states of the power-domain itself, we don't have any
> phandles to point to.
>
OK
>>> + The integer value '0' represents the lowest performance level and the higher
>>> + values represent higher performance levels.
>>
>> needs to be changed as OPP table entry.
>
> Not sure I understood what change you are looking for :(
>
Looks like I commented the same thing below, just redundant comment
here. Sorry about that.
>>> When present in the OPP table of a
>>> + power-domain, it represents the performance level of the domain. When present
>>
>> again "performance level of the domain corresponding to that OPP entry"
>> on something similar
>
> Ok.
>
>>> + in the OPP table of a normal device, it represents the performance level of
>>
>> what do you mean by normal device ? needs description as that's
>> something new introduced here.
>
> It should be non-power-domain node.
>
OK
>>> + the parent power-domain. The OPP table can contain the
>>> + "domain-performance-state" property, only if the device node contains the
>>> + "power-domains" or "#power-domain-cells" property.
>>
>> Why such a restriction ?
>
> Why would we use it for non-power-domain cases? That's not what we
> are looking for..
>
OK I was imagining that this would abstract clocks and regulators and
hence thinking of other possibilities.
>>> The OPP nodes aren't
>>> + allowed to contain the "domain-performance-state" property partially, i.e.
>>> + Either all OPP nodes in the OPP table have the "domain-performance-state"
>>> + property or none of them have it.
>>> +
>>> Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
>>>
>>> / {
>>> @@ -528,3 +540,60 @@ Example 5: opp-supported-hw
>>> };
>>> };
>>> };
>>> +
>>> +Example 7: domain-Performance-state:
>>> +(example: For 1GHz require domain state 1 and for 1.1 & 1.2 GHz require state 2)
>>> +
>>> +/ {
>>> + domain_opp_table: opp_table0 {
>>> + compatible = "operating-points-v2";
>>> +
>>> + opp@1 {
>>> + domain-performance-state = <1>;
>>> + opp-microvolt = <975000 970000 985000>;
>>> + };
>>> + opp@2 {
>>> + domain-performance-state = <2>;
>>> + opp-microvolt = <1075000 1000000 1085000>;
>>> + };
>>> + };
>>> +
>>> + foo_domain: power-controller@12340000 {
>>> + compatible = "foo,power-controller";
>>> + reg = <0x12340000 0x1000>;
>>> + #power-domain-cells = <0>;
>>> + operating-points-v2 = <&domain_opp_table>;
>>
>> How does it scale with power domain providers with multiple power domain ?
>
> Devices can't have multiple power domains today. Will see this when
> that support is added.
>
Agreed and I see some working already happening on that, so yes we can
add that later.
What I was referring is about power domain provider with multiple power
domains(simply #power-domain-cells=<1> case as explained in the
power-domain specification.
> Note that only the power domains can have multiple parent power
> domains today.
>
>>> + }
>>> +
>>> + cpu0_opp_table: opp_table1 {
>>> + compatible = "operating-points-v2";
>>> + opp-shared;
>>> +
>>> + opp@1000000000 {
>>> + opp-hz = /bits/ 64 <1000000000>;
>>> + domain-performance-state = <1>;
>>> + };
>>> + opp@1100000000 {
>>> + opp-hz = /bits/ 64 <1100000000>;
>>> + domain-performance-state = <2>;
>>> + };
>>> + opp@1200000000 {
>>> + opp-hz = /bits/ 64 <1200000000>;
>>> + domain-performance-state = <2>;
>>> + };
>>> + };
>>> +
>>> + cpus {
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + cpu@0 {
>>> + compatible = "arm,cortex-a9";
>>> + reg = <0>;
>>> + clocks = <&clk_controller 0>;
>>> + clock-names = "cpu";
>>> + operating-points-v2 = <&cpu0_opp_table>;
>>
>> Do we ignore operating-points-v2 above as this device/cpu node contains
>> power domain which has operating-points-v2 property ? In other words
>> how do they correlate ?
>
> Devices and their power domains can both have their performance
> states. Just that to get the device in a particular state, we may need
> to get its power domain to a particular state first.
>
Yes. To simplify what not we just have power-domain for a device and
change state of that domain to change the performance of that device.
Then put this in the hierarchy. Some thing similar to what we already
have with new domain-idle states. In that way, we can move any
performance control to the domain and abstract the clocks and regulators
from the devices as the first step and from the OSPM view if there's
firmware support.
If we are looking this power-domains with performance as just some
*advanced regulators*, I don't like the complexity added.
I am also looking at how does this align with other specifications like
ACPI and SCMI that are trying to solve similar issues.
--
Regards,
Sudeep
^ permalink raw reply
* [PATCH 4/4] net: macb: Add macb_ptp to compilation chain
From: Rafal Ozieblo @ 2017-04-13 13:39 UTC (permalink / raw)
To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree,
linux-arm-kernel, harinikatakamlinux, harini.katakam,
richardcochran, Andrei.Pistirica
Cc: Rafal Ozieblo
In-Reply-To: <1492090439-11793-1-git-send-email-rafalo@cadence.com>
Add macb_ptp.c to Makefile.
In case that macb is compiled as a module, it has been renamed to
cadence-macb.ko to avoid naming confusion in Makefile.
Signed-off-by: Rafal Ozieblo <rafalo@cadence.com>
---
drivers/net/ethernet/cadence/Makefile | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/cadence/Makefile b/drivers/net/ethernet/cadence/Makefile
index 4ba7559..a7f6e04 100644
--- a/drivers/net/ethernet/cadence/Makefile
+++ b/drivers/net/ethernet/cadence/Makefile
@@ -1,6 +1,11 @@
#
# Makefile for the Atmel network device drivers.
#
+cadence-macb-y := macb.o
-obj-$(CONFIG_MACB) += macb.o
+ifeq ($(CONFIG_MACB_USE_HWSTAMP),y)
+cadence-macb-y += macb_ptp.o
+endif
+
+obj-$(CONFIG_MACB) += cadence-macb.o
obj-$(CONFIG_MACB_PCI) += macb_pci.o
--
2.4.5
^ permalink raw reply related
* [PATCH 3/4] net: macb: Add hardware PTP support
From: Rafal Ozieblo @ 2017-04-13 13:39 UTC (permalink / raw)
To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree,
linux-arm-kernel, harinikatakamlinux, harini.katakam,
richardcochran, Andrei.Pistirica
Cc: Rafal Ozieblo
In-Reply-To: <1492090439-11793-1-git-send-email-rafalo@cadence.com>
This patch is based on original Harini's patch and Andrei's patch,
implemented in a separate file to ease the review/maintanance
and integration with other platforms.
This driver does support GEM-GXL:
- Register ptp clock framework
- Initialize PTP related registers
- HW time stamp on the PTP Ethernet packets are received using the
SO_TIMESTAMPING API. Where timers are obtained from the dma buffer
descriptors
Signed-off-by: Rafal Ozieblo <rafalo@cadence.com>
---
drivers/net/ethernet/cadence/macb.c | 99 ++++-
drivers/net/ethernet/cadence/macb.h | 140 ++++++
drivers/net/ethernet/cadence/macb_ptp.c | 724 ++++++++++++++++++++++++++++++++
3 files changed, 958 insertions(+), 5 deletions(-)
create mode 100755 drivers/net/ethernet/cadence/macb_ptp.c
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 59d459b..603bac1 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -826,6 +826,15 @@ static void macb_tx_interrupt(struct macb_queue *queue)
/* First, update TX stats if needed */
if (skb) {
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ if (gem_ptp_do_txstamp(queue, skb, desc) == 0) {
+ /* skb now belongs to timestamp buffer
+ * and will be removed later
+ */
+ tx_skb->skb = NULL;
+ schedule_work(&queue->tx_ts_task);
+ }
+#endif
netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
macb_tx_ring_wrap(bp, tail),
skb->data);
@@ -992,6 +1001,10 @@ static int gem_rx(struct macb *bp, int budget)
bp->dev->stats.rx_packets++;
bp->dev->stats.rx_bytes += skb->len;
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ gem_ptp_do_rxstamp(bp, skb, desc);
+#endif
+
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
skb->len, skb->csum);
@@ -1314,6 +1327,11 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
queue_writel(queue, ISR, MACB_BIT(HRESP));
}
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ if (status & MACB_PTP_INT_MASK)
+ macb_ptp_int(queue, status);
+#endif
+
status = queue_readl(queue, ISR);
}
@@ -1643,8 +1661,10 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Make newly initialized descriptor visible to hardware */
wmb();
-
- skb_tx_timestamp(skb);
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ if (!bp->ptp_hw_support)
+#endif
+ skb_tx_timestamp(skb);
macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
@@ -2502,6 +2522,71 @@ static int macb_set_ringparam(struct net_device *netdev,
return 0;
}
+#ifdef CONFIG_MACB_USE_HWSTAMP
+static unsigned int gem_get_tsu_rate(struct macb *bp)
+{
+ struct clk *tsu_clk;
+ unsigned int tsu_rate;
+
+ tsu_clk = devm_clk_get(&bp->pdev->dev, "tsu_clk");
+ if (!IS_ERR(tsu_clk))
+ tsu_rate = clk_get_rate(tsu_clk);
+ /* try pclk instead */
+ else if (!IS_ERR(bp->pclk)) {
+ tsu_clk = bp->pclk;
+ tsu_rate = clk_get_rate(tsu_clk);
+ } else
+ return -ENOTSUPP;
+ return tsu_rate;
+}
+
+static s32 gem_get_ptp_max_adj(void)
+{
+ return 64E6;
+}
+
+static int gem_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct macb *bp = netdev_priv(dev);
+
+ ethtool_op_get_ts_info(dev, info);
+ if (!bp->ptp_hw_support)
+ return 0;
+
+ info->so_timestamping =
+ SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ info->tx_types =
+ (1 << HWTSTAMP_TX_ONESTEP_SYNC) |
+ (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON);
+ info->rx_filters =
+ (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_ALL);
+ info->phc_index = -1;
+
+ if (bp->ptp_clock)
+ info->phc_index = ptp_clock_index(bp->ptp_clock);
+
+ return 0;
+}
+
+static struct macb_ptp_info gem_ptp_info = {
+ .ptp_init = gem_ptp_init,
+ .ptp_remove = gem_ptp_remove,
+ .get_ptp_max_adj = gem_get_ptp_max_adj,
+ .get_tsu_rate = gem_get_tsu_rate,
+ .get_ts_info = gem_get_ts_info,
+ .get_hwtst = gem_get_hwtst,
+ .set_hwtst = gem_set_hwtst,
+};
+#endif
+
static int macb_get_ts_info(struct net_device *netdev,
struct ethtool_ts_info *info)
{
@@ -2642,8 +2727,8 @@ static void macb_configure_caps(struct macb *bp,
if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5)))
pr_err("GEM doesn't support hardware ptp.\n");
else {
- pr_emerg("rozieblo: ptp_hw_support = true");
bp->ptp_hw_support = true;
+ bp->ptp_info = &gem_ptp_info;
}
}
#endif
@@ -3252,7 +3337,9 @@ static const struct macb_config np4_config = {
};
static const struct macb_config zynqmp_config = {
- .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO,
+ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE |
+ MACB_CAPS_JUMBO |
+ MACB_CAPS_GEM_HAS_PTP,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
@@ -3286,7 +3373,9 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids);
#endif /* CONFIG_OF */
static const struct macb_config default_gem_config = {
- .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO,
+ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE |
+ MACB_CAPS_JUMBO |
+ MACB_CAPS_GEM_HAS_PTP,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 2606970..5295045 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -11,6 +11,9 @@
#define _MACB_H
#include <linux/phy.h>
+#include <linux/ptp_clock.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/net_tstamp.h>
#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP)
#define MACB_EXT_DESC
@@ -90,6 +93,10 @@
#define GEM_SA3T 0x009C /* Specific3 Top */
#define GEM_SA4B 0x00A0 /* Specific4 Bottom */
#define GEM_SA4T 0x00A4 /* Specific4 Top */
+#define GEM_EFTSH 0x00e8 /* PTP Event Frame Transmitted Seconds Register 47:32 */
+#define GEM_EFRSH 0x00ec /* PTP Event Frame Received Seconds Register 47:32 */
+#define GEM_PEFTSH 0x00f0 /* PTP Peer Event Frame Transmitted Seconds Register 47:32 */
+#define GEM_PEFRSH 0x00f4 /* PTP Peer Event Frame Received Seconds Register 47:32 */
#define GEM_OTX 0x0100 /* Octets transmitted */
#define GEM_OCTTXL 0x0100 /* Octets transmitted [31:0] */
#define GEM_OCTTXH 0x0104 /* Octets transmitted [47:32] */
@@ -159,6 +166,9 @@
#define GEM_DCFG6 0x0294 /* Design Config 6 */
#define GEM_DCFG7 0x0298 /* Design Config 7 */
+#define GEM_TXBDCTRL 0x04cc /* TX Buffer Descriptor control register */
+#define GEM_RXBDCTRL 0x04d0 /* RX Buffer Descriptor control register */
+
#define GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2))
#define GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2))
#define GEM_TBQPH(hw_q) (0x04C8)
@@ -195,6 +205,8 @@
#define MACB_TZQ_OFFSET 12 /* Transmit zero quantum pause frame */
#define MACB_TZQ_SIZE 1
#define MACB_SRTSM_OFFSET 15
+#define MACB_OSSMODE_OFFSET 24 /* Enable One Step Synchro Mode */
+#define MACB_OSSMODE_SIZE 1
/* Bitfields in NCFGR */
#define MACB_SPD_OFFSET 0 /* Speed */
@@ -362,6 +374,16 @@
#define MACB_PDRSFT_SIZE 1
#define MACB_SRI_OFFSET 26 /* TSU Seconds Register Increment */
#define MACB_SRI_SIZE 1
+#define MACB_TCI_OFFSET 29 /* TSU timer comparison interrupt */
+#define MACB_TCI_SIZE 1
+#define MACB_PTP_INT_MASK (MACB_BIT(DRQFR) \
+ | MACB_BIT(SFR) \
+ | MACB_BIT(DRQFT) \
+ | MACB_BIT(SFT) \
+ | MACB_BIT(PDRQFR) \
+ | MACB_BIT(PDRSFR) \
+ | MACB_BIT(PDRQFT) \
+ | MACB_BIT(PDRSFT))
/* Timer increment fields */
#define MACB_TI_CNS_OFFSET 0
@@ -452,6 +474,52 @@
#define GEM_NSINCR_OFFSET 0
#define GEM_NSINCR_SIZE 8
+/* Bitfields in TSH */
+#define GEM_TSH_OFFSET 0 /* TSU timer value (s). MSB [47:32] of seconds timer count */
+#define GEM_TSH_SIZE 16
+
+/* Bitfields in TSL */
+#define GEM_TSL_OFFSET 0 /* TSU timer value (s). LSB [31:0] of seconds timer count */
+#define GEM_TSL_SIZE 32
+
+/* Bitfields in TN */
+#define GEM_TN_OFFSET 0 /* TSU timer value (ns) */
+#define GEM_TN_SIZE 30
+
+/* Bitfields in TXBDCTRL */
+#define GEM_TXTSMODE_OFFSET 4 /* TX Descriptor Timestamp Insertion mode */
+#define GEM_TXTSMODE_SIZE 2
+
+/* Bitfields in RXBDCTRL */
+#define GEM_RXTSMODE_OFFSET 4 /* RX Descriptor Timestamp Insertion mode */
+#define GEM_RXTSMODE_SIZE 2
+
+/* Transmit DMA buffer descriptor Word 1 */
+#define GEM_DMA_TXVALID_OFFSET 23 /* timestamp has been captured in the Buffer Descriptor */
+#define GEM_DMA_TXVALID_SIZE 1
+
+/* Receive DMA buffer descriptor Word 0 */
+#define GEM_DMA_RXVALID_OFFSET 2 /* indicates a valid timestamp in the Buffer Descriptor */
+#define GEM_DMA_RXVALID_SIZE 1
+
+/* DMA buffer descriptor Word 2 (32 bit addressing) or Word 4 (64 bit addressing) */
+#define GEM_DMA_SECL_OFFSET 30 /* Timestamp seconds[1:0] */
+#define GEM_DMA_SECL_SIZE 2
+#define GEM_DMA_NSEC_OFFSET 0 /* Timestamp nanosecs [29:0] */
+#define GEM_DMA_NSEC_SIZE 30
+
+/* DMA buffer descriptor Word 3 (32 bit addressing) or Word 5 (64 bit addressing) */
+
+/* New hardware supports 12 bit precision of timestamp in DMA buffer descriptor.
+ * Old hardware supports only 6 bit precision but it is enough for PTP.
+ * Less accuracy is used always instead of checking hardware version.
+ */
+#define GEM_DMA_SECH_OFFSET 0 /* Timestamp seconds[5:2] */
+#define GEM_DMA_SECH_SIZE 4
+#define GEM_DMA_SEC_WIDTH (GEM_DMA_SECH_SIZE + GEM_DMA_SECL_SIZE)
+#define GEM_DMA_SEC_TOP (1 << GEM_DMA_SEC_WIDTH)
+#define GEM_DMA_SEC_MASK (GEM_DMA_SEC_TOP - 1)
+
/* Bitfields in ADJ */
#define GEM_ADDSUB_OFFSET 31
#define GEM_ADDSUB_SIZE 1
@@ -527,6 +595,8 @@
#define queue_readl(queue, reg) (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg)
#define queue_writel(queue, reg, value) (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value))
+#define PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */
+
/* Conditional GEM/MACB macros. These perform the operation to the correct
* register dependent on whether the device is a GEM or a MACB. For registers
* and bitfields that are common across both devices, use macb_{read,write}l
@@ -889,6 +959,18 @@ struct macb_config {
int jumbo_max_len;
};
+#ifdef CONFIG_MACB_USE_HWSTAMP
+struct tsu_incr {
+ u32 sub_ns;
+ u32 ns;
+};
+
+struct gem_tx_ts {
+ struct sk_buff *skb;
+ struct macb_dma_desc_ptp desc_ptp;
+};
+#endif
+
struct macb_queue {
struct macb *bp;
int irq;
@@ -905,6 +987,12 @@ struct macb_queue {
struct macb_tx_skb *tx_skb;
dma_addr_t tx_ring_dma;
struct work_struct tx_error_task;
+
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ struct work_struct tx_ts_task;
+ unsigned int tx_ts_head, tx_ts_tail;
+ struct gem_tx_ts tx_timestamps[PTP_TS_BUFFER_SIZE];
+#endif
};
struct macb {
@@ -978,9 +1066,61 @@ struct macb {
#ifdef CONFIG_MACB_USE_HWSTAMP
bool ptp_hw_support;
+ spinlock_t tsu_clk_lock; /* gem tsu clock locking */
+ unsigned int tsu_rate;
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_clock_info;
+ struct tsu_incr tsu_incr;
+ struct hwtstamp_config tstamp_config;
#endif
};
+#ifdef CONFIG_MACB_USE_HWSTAMP
+#define GEM_TSEC_SIZE (GEM_TSH_SIZE + GEM_TSL_SIZE)
+#define TSU_SEC_MAX_VAL (((u64)1 << GEM_TSEC_SIZE) - 1)
+#define TSU_NSEC_MAX_VAL ((1 << GEM_TN_SIZE) - 1)
+
+enum macb_bd_control {
+ TSTAMP_DISABLED,
+ TSTAMP_FRAME_PTP_EVENT_ONLY,
+ TSTAMP_ALL_PTP_FRAMES,
+ TSTAMP_ALL_FRAMES,
+};
+
+void gem_ptp_init(struct net_device *ndev);
+void gem_ptp_remove(struct net_device *ndev);
+int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *des);
+void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc);
+void macb_ptp_int(struct macb_queue *queue, u32 status);
+static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc)
+{
+ if (queue->bp->tstamp_config.tx_type == TSTAMP_DISABLED)
+ return -ENOTSUPP;
+
+ return gem_ptp_txstamp(queue, skb, desc);
+}
+
+static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc)
+{
+ if (bp->tstamp_config.rx_filter == TSTAMP_DISABLED)
+ return;
+
+ gem_ptp_rxstamp(bp, skb, desc);
+}
+int gem_get_hwtst(struct net_device *dev, struct ifreq *rq);
+int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd);
+#else
+static inline void gem_ptp_init(struct net_device *ndev) { }
+static inline void gem_ptp_remove(struct net_device *ndev) { }
+
+static inline int gem_ptp_do_txstamp(struct macb_queue *queue, struct sk_buff *skb, struct macb_dma_desc *desc)
+{
+ return 0;
+}
+
+static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, struct macb_dma_desc *desc) { }
+#endif
+
static inline bool macb_is_gem(struct macb *bp)
{
return !!(bp->caps & MACB_CAPS_MACB_IS_GEM);
diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c
new file mode 100755
index 0000000..72a79c4
--- /dev/null
+++ b/drivers/net/ethernet/cadence/macb_ptp.c
@@ -0,0 +1,724 @@
+/**
+ * 1588 PTP support for Cadence GEM device.
+ *
+ * Copyright (C) 2017 Cadence Design Systems - http://www.cadence.com
+ *
+ * Authors: Rafal Ozieblo <rafalo@cadence.com>
+ * Bartosz Folta <bfolta@cadence.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 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/time64.h>
+#include <linux/ptp_classify.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/net_tstamp.h>
+#include <linux/circ_buf.h>
+#include <linux/spinlock.h>
+
+#include "macb.h"
+
+#define GEM_PTP_TIMER_NAME "gem-ptp-timer"
+
+static struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp,
+ struct macb_dma_desc *desc)
+{
+ if (bp->hw_dma_cap == HW_DMA_CAP_PTP)
+ return (struct macb_dma_desc_ptp *)
+ ((u8 *)desc + sizeof(struct macb_dma_desc));
+ if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP)
+ return (struct macb_dma_desc_ptp *)
+ ((u8 *)desc + sizeof(struct macb_dma_desc)
+ + sizeof(struct macb_dma_desc_64));
+ return NULL;
+}
+
+static int gem_tsu_get_time(struct macb *bp, struct timespec64 *ts)
+{
+ long first, second;
+ u32 secl, sech;
+ unsigned long flags;
+
+ if (!bp || !ts)
+ return -EINVAL;
+
+ spin_lock_irqsave(&bp->tsu_clk_lock, flags);
+ first = gem_readl(bp, TN);
+ secl = gem_readl(bp, TSL);
+ sech = gem_readl(bp, TSH);
+ second = gem_readl(bp, TN);
+
+ /* test for nsec rollover */
+ if (first > second) {
+ /* if so, use later read & re-read seconds
+ * (assume all done within 1s)
+ */
+ ts->tv_nsec = gem_readl(bp, TN);
+ secl = gem_readl(bp, TSL);
+ sech = gem_readl(bp, TSH);
+ } else
+ ts->tv_nsec = first;
+
+ ts->tv_sec = (((u64)sech << GEM_TSL_SIZE) | secl)
+ & TSU_SEC_MAX_VAL;
+
+ spin_unlock_irqrestore(&bp->tsu_clk_lock, flags);
+ return 0;
+}
+
+static int gem_tsu_set_time(struct macb *bp, const struct timespec64 *ts)
+{
+ u32 ns, sech, secl;
+ unsigned long flags;
+
+ if (!bp || !ts)
+ return -EINVAL;
+
+ secl = (u32)ts->tv_sec;
+ sech = (ts->tv_sec >> GEM_TSL_SIZE) & ((1 << GEM_TSH_SIZE) - 1);
+ ns = ts->tv_nsec;
+
+ spin_lock_irqsave(&bp->tsu_clk_lock, flags);
+
+ /* TSH doesn't latch the time and no atomicity! */
+ gem_writel(bp, TN, 0); /* clear to avoid overflow */
+ gem_writel(bp, TSH, sech);
+ /* write lower bits 2nd, for synchronized secs update */
+ gem_writel(bp, TSL, secl);
+ gem_writel(bp, TN, ns);
+
+ spin_unlock_irqrestore(&bp->tsu_clk_lock, flags);
+
+ return 0;
+}
+
+static int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec)
+{
+ unsigned long flags;
+
+ if (!bp || !incr_spec)
+ return -EINVAL;
+
+ /* tsu_timer_incr register must be written after
+ * the tsu_timer_incr_sub_ns register and the write operation
+ * will cause the value written to the tsu_timer_incr_sub_ns register
+ * to take effect.
+ */
+ spin_lock_irqsave(&bp->tsu_clk_lock, flags);
+ gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, incr_spec->sub_ns));
+ gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns));
+ spin_unlock_irqrestore(&bp->tsu_clk_lock, flags);
+
+ return 0;
+}
+
+static int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct tsu_incr incr_spec;
+ struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);
+ u64 adj;
+ u32 word;
+ bool neg_adj = false;
+
+ if (!ptp)
+ return -EINVAL;
+
+ if (scaled_ppm < 0) {
+ neg_adj = true;
+ scaled_ppm = -scaled_ppm;
+ }
+
+ /* Adjustment is relative to base frequency */
+ incr_spec.sub_ns = bp->tsu_incr.sub_ns;
+ incr_spec.ns = bp->tsu_incr.ns;
+
+ /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */
+ word = ((u64)incr_spec.ns << GEM_SUBNSINCR_SIZE) + incr_spec.sub_ns;
+ adj = (u64)scaled_ppm * word;
+ /* Divide with rounding, equivalent to floating dividing:
+ * (temp / USEC_PER_SEC) + 0.5
+ */
+ adj += (USEC_PER_SEC >> 1);
+ adj >>= GEM_SUBNSINCR_SIZE; /* remove fractions */
+ adj = div_u64(adj, USEC_PER_SEC);
+ adj = neg_adj ? (word - adj) : (word + adj);
+
+ incr_spec.ns = (adj >> GEM_SUBNSINCR_SIZE)
+ & ((1 << GEM_NSINCR_SIZE) - 1);
+ incr_spec.sub_ns = adj & ((1 << GEM_SUBNSINCR_SIZE) - 1);
+ gem_tsu_incr_set(bp, &incr_spec);
+ return 0;
+}
+
+static int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct timespec64 now, then = ns_to_timespec64(delta);
+ struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);
+ u32 adj, sign = 0;
+
+ if (!ptp)
+ return -EINVAL;
+
+ if (delta < 0) {
+ sign = 1;
+ delta = -delta;
+ }
+
+ if (delta > TSU_NSEC_MAX_VAL) {
+ gem_tsu_get_time(bp, &now);
+ if (sign)
+ now = timespec64_sub(now, then);
+ else
+ now = timespec64_add(now, then);
+
+ gem_tsu_set_time(bp, (const struct timespec64 *)&now);
+ } else {
+ adj = (sign << GEM_ADDSUB_OFFSET) | delta;
+
+ gem_writel(bp, TA, adj);
+ }
+
+ return 0;
+}
+
+static int gem_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+ struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);
+
+ if (!ptp || !ts)
+ return -EINVAL;
+
+ gem_tsu_get_time(bp, ts);
+ return 0;
+}
+
+static int gem_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);
+
+ if (!ptp || !ts)
+ return -EINVAL;
+
+ gem_tsu_set_time(bp, ts);
+ return 0;
+}
+
+static int gem_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);
+
+ if (!ptp || !rq)
+ return -EINVAL;
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_EXTTS: /* Toggle TSU match interrupt */
+ if (on)
+ macb_writel(bp, IER, MACB_BIT(TCI));
+ else
+ macb_writel(bp, IDR, MACB_BIT(TCI));
+ break;
+ case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */
+ return -EOPNOTSUPP;
+ /* break; */
+ case PTP_CLK_REQ_PPS: /* Toggle TSU periodic (second) interrupt */
+ if (on)
+ macb_writel(bp, IER, MACB_BIT(SRI));
+ else
+ macb_writel(bp, IDR, MACB_BIT(SRI));
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct ptp_clock_info gem_ptp_caps_template = {
+ .owner = THIS_MODULE,
+ .name = GEM_PTP_TIMER_NAME,
+ .max_adj = 0,
+ .n_alarm = 1,
+ .n_ext_ts = 1,
+ .n_per_out = 0,
+ .n_pins = 0,
+ .pps = 1,
+ .adjfine = gem_ptp_adjfine,
+ .adjtime = gem_ptp_adjtime,
+ .gettime64 = gem_ptp_gettime,
+ .settime64 = gem_ptp_settime,
+ .enable = gem_ptp_enable,
+};
+
+static void gem_ptp_init_timer(struct macb *bp)
+{
+ u32 rem = 0;
+
+ bp->tsu_incr.ns = div_u64_rem(NSEC_PER_SEC, bp->tsu_rate, &rem);
+ if (rem) {
+ u64 adj = rem;
+
+ adj <<= GEM_SUBNSINCR_SIZE;
+ bp->tsu_incr.sub_ns = div_u64(adj, bp->tsu_rate);
+ } else {
+ bp->tsu_incr.sub_ns = 0;
+ }
+}
+
+static void gem_ptp_init_tsu(struct macb *bp)
+{
+ struct timespec64 ts;
+
+ /* 1. get current system time */
+ ts = ns_to_timespec64(ktime_to_ns(ktime_get_real()));
+
+ /* 2. set ptp timer */
+ gem_tsu_set_time(bp, &ts);
+
+ /* 3. set PTP timer increment value to BASE_INCREMENT */
+ gem_tsu_incr_set(bp, &bp->tsu_incr);
+
+ gem_writel(bp, TA, 0);
+}
+
+static void gem_ptp_clear_timer(struct macb *bp)
+{
+ bp->tsu_incr.ns = 0;
+ bp->tsu_incr.sub_ns = 0;
+
+ gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, 0));
+ gem_writel(bp, TI, GEM_BF(NSINCR, 0));
+ gem_writel(bp, TA, 0);
+}
+
+static int gem_hw_timestamp(struct macb *bp,
+ u32 dma_desc_ts_1, u32 dma_desc_ts_2, struct timespec64 *ts)
+{
+ struct timespec64 tsu;
+
+ ts->tv_sec = (GEM_BFEXT(DMA_SECH, dma_desc_ts_2) << GEM_DMA_SECL_SIZE) |
+ GEM_BFEXT(DMA_SECL, dma_desc_ts_1);
+ ts->tv_nsec = GEM_BFEXT(DMA_NSEC, dma_desc_ts_1);
+
+ /* TSU overlaping workaround
+ * The timestamp only contains lower few bits of seconds,
+ * so add value from 1588 timer
+ */
+ gem_tsu_get_time(bp, &tsu);
+
+ /* If the top bit is set in the timestamp,
+ * but not in 1588 timer, it has rolled over,
+ * so subtract max size
+ */
+ if ((ts->tv_sec & (GEM_DMA_SEC_TOP >> 1)) &&
+ !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1)))
+ ts->tv_sec -= GEM_DMA_SEC_TOP;
+
+ ts->tv_sec += ((~GEM_DMA_SEC_MASK) & (tsu.tv_sec));
+
+ return 0;
+}
+
+void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb,
+ struct macb_dma_desc *desc)
+{
+ struct timespec64 ts;
+ struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
+ struct macb_dma_desc_ptp *desc_ptp;
+
+ if (GEM_BFEXT(DMA_RXVALID, desc->addr)) {
+ desc_ptp = macb_ptp_desc(bp, desc);
+ gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts);
+ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+ shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+ }
+}
+
+static void gem_tstamp_tx(struct macb *bp, struct sk_buff *skb,
+ struct macb_dma_desc_ptp *desc_ptp)
+{
+ struct skb_shared_hwtstamps shhwtstamps;
+ struct timespec64 ts;
+
+ gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts);
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+ skb_tstamp_tx(skb, &shhwtstamps);
+}
+
+int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb,
+ struct macb_dma_desc *desc)
+{
+ struct gem_tx_ts *tx_timestamp;
+ struct macb_dma_desc_ptp *desc_ptp;
+ unsigned long head = queue->tx_ts_head;
+ unsigned long tail = READ_ONCE(queue->tx_ts_tail);
+
+ if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl))
+ return -EINVAL;
+
+ if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0)
+ return -ENOMEM;
+
+ desc_ptp = macb_ptp_desc(queue->bp, desc);
+ tx_timestamp = &queue->tx_timestamps[head];
+ tx_timestamp->skb = skb;
+ tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1;
+ tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2;
+ /* move head */
+ smp_store_release(&queue->tx_ts_head,
+ (head + 1) & (PTP_TS_BUFFER_SIZE - 1));
+ return 0;
+}
+
+static void gem_tx_timestamp_flush(struct work_struct *work)
+{
+ struct macb_queue *queue =
+ container_of(work, struct macb_queue, tx_ts_task);
+ struct gem_tx_ts *tx_ts;
+ unsigned long head, tail;
+
+ /* take current head */
+ head = smp_load_acquire(&queue->tx_ts_head);
+ tail = queue->tx_ts_tail;
+
+ while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) {
+ tx_ts = &queue->tx_timestamps[tail];
+ gem_tstamp_tx(queue->bp, tx_ts->skb, &tx_ts->desc_ptp);
+ /* cleanup */
+ dev_kfree_skb_any(tx_ts->skb);
+ /* remove old tail */
+ smp_store_release(&queue->tx_ts_tail,
+ (tail + 1) & (PTP_TS_BUFFER_SIZE - 1));
+ tail = queue->tx_ts_tail;
+ }
+}
+
+void gem_ptp_init(struct net_device *dev)
+{
+ struct macb *bp = netdev_priv(dev);
+ unsigned int q;
+ struct macb_queue *queue;
+
+ bp->ptp_clock_info = gem_ptp_caps_template;
+
+ /* nominal frequency and maximum adjustment in ppb */
+ bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp);
+ bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj();
+ gem_ptp_init_timer(bp);
+ bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &dev->dev);
+ if (IS_ERR(&bp->ptp_clock)) {
+ bp->ptp_clock = NULL;
+ pr_err("ptp clock register failed\n");
+ return;
+ }
+
+ spin_lock_init(&bp->tsu_clk_lock);
+ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+ queue->tx_ts_head = 0;
+ queue->tx_ts_tail = 0;
+ INIT_WORK(&queue->tx_ts_task, gem_tx_timestamp_flush);
+ queue_writel(queue, IER, MACB_PTP_INT_MASK);
+ }
+
+ gem_ptp_init_tsu(bp);
+
+ dev_info(&bp->pdev->dev, "%s ptp clock registered.\n",
+ GEM_PTP_TIMER_NAME);
+}
+
+void gem_ptp_remove(struct net_device *ndev)
+{
+ struct macb *bp = netdev_priv(ndev);
+
+ if (bp->ptp_clock)
+ ptp_clock_unregister(bp->ptp_clock);
+
+ gem_ptp_clear_timer(bp);
+
+ dev_info(&bp->pdev->dev, "%s ptp clock unregistered.\n",
+ GEM_PTP_TIMER_NAME);
+}
+
+static int gem_ptp_set_ts_mode(struct macb *bp,
+ enum macb_bd_control tx_bd_control,
+ enum macb_bd_control rx_bd_control)
+{
+ if (!bp)
+ return -EINVAL;
+
+ gem_writel(bp, TXBDCTRL, GEM_BF(TXTSMODE, tx_bd_control));
+ gem_writel(bp, RXBDCTRL, GEM_BF(RXTSMODE, rx_bd_control));
+
+ return 0;
+}
+
+int gem_get_hwtst(struct net_device *dev, struct ifreq *rq)
+{
+ struct macb *bp = netdev_priv(dev);
+ struct hwtstamp_config *tstamp_config = &bp->tstamp_config;
+
+ if (!bp->ptp_hw_support)
+ return -EFAULT;
+
+ if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config)))
+ return -EFAULT;
+ else
+ return 0;
+}
+
+static int gem_ptp_set_one_step_sync(struct macb *bp, u8 enable)
+{
+ u32 reg_val;
+
+ if (!bp || enable > 1)
+ return -EINVAL;
+
+ reg_val = macb_readl(bp, NCR);
+
+ if (enable)
+ macb_writel(bp, NCR, reg_val | MACB_BIT(OSSMODE));
+ else
+ macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE));
+
+ return 0;
+}
+
+int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct macb *bp = netdev_priv(dev);
+ struct hwtstamp_config *tstamp_config = &bp->tstamp_config;
+ enum macb_bd_control tx_bd_control = TSTAMP_DISABLED;
+ enum macb_bd_control rx_bd_control = TSTAMP_DISABLED;
+ u32 regval;
+
+ if (!bp->ptp_hw_support)
+ return -EFAULT;
+
+ if (copy_from_user(tstamp_config, ifr->ifr_data,
+ sizeof(*tstamp_config)))
+ return -EFAULT;
+
+ /* reserved for future extensions */
+ if (tstamp_config->flags)
+ return -EINVAL;
+
+ switch (tstamp_config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ break;
+ case HWTSTAMP_TX_ONESTEP_SYNC:
+ if (gem_ptp_set_one_step_sync(bp, 1) != 0)
+ return -ERANGE;
+ case HWTSTAMP_TX_ON:
+ tx_bd_control = TSTAMP_ALL_FRAMES;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (tstamp_config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ rx_bd_control = TSTAMP_ALL_PTP_FRAMES;
+ tstamp_config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ regval = macb_readl(bp, NCR);
+ macb_writel(bp, NCR, (regval | MACB_BIT(SRTSM)));
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_ALL:
+ rx_bd_control = TSTAMP_ALL_FRAMES;
+ tstamp_config->rx_filter = HWTSTAMP_FILTER_ALL;
+ break;
+ default:
+ tstamp_config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0)
+ return -ERANGE;
+
+ if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config)))
+ return -EFAULT;
+ else
+ return 0;
+}
+
+static int gem_ptp_time_peer_frame_tx_get(struct macb *bp,
+ struct timespec64 *ts)
+{
+ if (!bp || !ts)
+ return -EINVAL;
+
+ ts->tv_sec = (((u64)gem_readl(bp, PEFTSH) << 32) |
+ gem_readl(bp, PEFTSL)) & TSU_SEC_MAX_VAL;
+ ts->tv_nsec = gem_readl(bp, PEFTN);
+
+ return 0;
+}
+
+static int gem_ptp_time_peer_frame_rx_get(struct macb *bp,
+ struct timespec64 *ts)
+{
+ if (!bp || !ts)
+ return -EINVAL;
+
+ ts->tv_sec = (((u64)gem_readl(bp, PEFRSH) << 32) |
+ gem_readl(bp, PEFRSL)) & TSU_SEC_MAX_VAL;
+ ts->tv_nsec = gem_readl(bp, PEFRN);
+
+ return 0;
+}
+
+static int gem_ptp_time_frame_tx_get(struct macb *bp, struct timespec64 *ts)
+{
+ if (!bp || !ts)
+ return -EINVAL;
+
+ ts->tv_sec = (((u64)gem_readl(bp, EFTSH) << 32) |
+ gem_readl(bp, EFTSL)) & TSU_SEC_MAX_VAL;
+ ts->tv_nsec = gem_readl(bp, EFTN);
+
+ return 0;
+}
+
+static int gem_ptp_time_frame_rx_get(struct macb *bp, struct timespec64 *ts)
+{
+ if (!bp || !ts)
+ return -EINVAL;
+
+ ts->tv_sec = (((u64)gem_readl(bp, EFRSH) << 32) |
+ gem_readl(bp, EFRSL)) & TSU_SEC_MAX_VAL;
+ ts->tv_nsec = gem_readl(bp, EFRN);
+
+ return 0;
+}
+
+static int gem_ptp_event(struct macb *bp, struct timespec64 *ts)
+{
+ struct ptp_clock_event event;
+
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = 0;
+ event.timestamp = ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
+
+ ptp_clock_event(bp->ptp_clock, &event);
+
+ return 0;
+}
+
+void macb_ptp_int(struct macb_queue *queue, u32 status)
+{
+ struct macb *bp = queue->bp;
+ struct timespec64 ts;
+
+ if (status & MACB_BIT(DRQFR)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(DRQFR));
+ if (gem_ptp_time_frame_rx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(SFR)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(SFR));
+ if (gem_ptp_time_frame_rx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(DRQFT)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(DRQFT));
+ if (gem_ptp_time_frame_tx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(SFT)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(SFT));
+ if (gem_ptp_time_frame_tx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(PDRQFR)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(PDRQFR));
+ if (gem_ptp_time_peer_frame_rx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(PDRSFR)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(PDRSFR));
+ if (gem_ptp_time_peer_frame_rx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(PDRQFT)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR,
+ MACB_BIT(PDRQFT));
+ if (gem_ptp_time_peer_frame_tx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+
+ if (status & MACB_BIT(PDRSFT)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR,
+ MACB_BIT(PDRSFT));
+ if (gem_ptp_time_peer_frame_tx_get(bp, &ts) != 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ }
+ gem_ptp_event(bp, &ts);
+ }
+}
--
2.4.5
^ permalink raw reply related
* [PATCH 2/4] net: macb: Add tsu_clk to device tree
From: Rafal Ozieblo @ 2017-04-13 13:38 UTC (permalink / raw)
To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree,
linux-arm-kernel, harinikatakamlinux, harini.katakam,
richardcochran, Andrei.Pistirica
Cc: Rafal Ozieblo
In-Reply-To: <1492090439-11793-1-git-send-email-rafalo@cadence.com>
Signed-off-by: Rafal Ozieblo <rafalo@cadence.com>
---
Documentation/devicetree/bindings/net/macb.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt
index 1506e94..27966ae 100644
--- a/Documentation/devicetree/bindings/net/macb.txt
+++ b/Documentation/devicetree/bindings/net/macb.txt
@@ -22,6 +22,7 @@ Required properties:
Required elements: 'pclk', 'hclk'
Optional elements: 'tx_clk'
Optional elements: 'rx_clk' applies to cdns,zynqmp-gem
+ Optional elements: 'tsu_clk'
- clocks: Phandles to input clocks.
Optional properties for PHY child node:
--
2.4.5
^ permalink raw reply related
* [PATCH 1/4] net: macb: Add support for PTP timestamps in DMA descriptors
From: Rafal Ozieblo @ 2017-04-13 13:33 UTC (permalink / raw)
To: David Miller, nicolas.ferre, netdev, linux-kernel, devicetree,
linux-arm-kernel, harinikatakamlinux, harini.katakam,
richardcochran, Andrei.Pistirica
Cc: Rafal Ozieblo
This patch adds support for PTP timestamps in
DMA buffer descriptors. It checks capability at runtime
and uses appropriate buffer descriptor.
Signed-off-by: Rafal Ozieblo <rafalo@cadence.com>
---
drivers/net/ethernet/cadence/Kconfig | 10 ++-
drivers/net/ethernet/cadence/macb.c | 133 +++++++++++++++++++++++++++--------
drivers/net/ethernet/cadence/macb.h | 36 ++++++++--
3 files changed, 141 insertions(+), 38 deletions(-)
diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig
index 608bea1..427d65a 100644
--- a/drivers/net/ethernet/cadence/Kconfig
+++ b/drivers/net/ethernet/cadence/Kconfig
@@ -29,7 +29,15 @@ config MACB
support for the MACB/GEM chip.
To compile this driver as a module, choose M here: the module
- will be called macb.
+ will be macb.
+
+config MACB_USE_HWSTAMP
+ bool "Use IEEE 1588 hwstamp"
+ depends on MACB
+ default y
+ imply PTP_1588_CLOCK
+ ---help---
+ Enable IEEE 1588 Precision Time Protocol (PTP) support for MACB.
config MACB_PCI
tristate "Cadence PCI MACB/GEM support"
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 5cbd1e7..59d459b 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -79,33 +79,84 @@
#define MACB_HALT_TIMEOUT 1230
/* DMA buffer descriptor might be different size
- * depends on hardware configuration.
+ * depends on hardware configuration:
+ *
+ * 1. dma address width 32 bits:
+ * word 1: 32 bit address of Data Buffer
+ * word 2: control
+ *
+ * 2. dma address width 64 bits:
+ * word 1: 32 bit address of Data Buffer
+ * word 2: control
+ * word 3: upper 32 bit address of Data Buffer
+ * word 4: unused
+ *
+ * 3. dma address width 32 bits with hardware timestamping:
+ * word 1: 32 bit address of Data Buffer
+ * word 2: control
+ * word 3: timestamp word 1
+ * word 4: timestamp word 2
+ *
+ * 4. dma address width 64 bits with hardware timestamping:
+ * word 1: 32 bit address of Data Buffer
+ * word 2: control
+ * word 3: upper 32 bit address of Data Buffer
+ * word 4: unused
+ * word 5: timestamp word 1
+ * word 6: timestamp word 2
*/
static unsigned int macb_dma_desc_get_size(struct macb *bp)
{
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
- return sizeof(struct macb_dma_desc) + sizeof(struct macb_dma_desc_64);
+#ifdef MACB_EXT_DESC
+ unsigned int desc_size;
+
+ switch (bp->hw_dma_cap) {
+ case HW_DMA_CAP_64B:
+ desc_size = sizeof(struct macb_dma_desc)
+ + sizeof(struct macb_dma_desc_64);
+ break;
+ case HW_DMA_CAP_PTP:
+ desc_size = sizeof(struct macb_dma_desc)
+ + sizeof(struct macb_dma_desc_ptp);
+ break;
+ case HW_DMA_CAP_64B_PTP:
+ desc_size = sizeof(struct macb_dma_desc)
+ + sizeof(struct macb_dma_desc_64)
+ + sizeof(struct macb_dma_desc_ptp);
+ break;
+ default:
+ desc_size = sizeof(struct macb_dma_desc);
+ }
+ return desc_size;
#endif
return sizeof(struct macb_dma_desc);
}
-static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int idx)
+static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int desc_idx)
{
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- /* Dma buffer descriptor is 4 words length (instead of 2 words)
- * for 64b GEM.
- */
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
- idx <<= 1;
+#ifdef MACB_EXT_DESC
+ switch (bp->hw_dma_cap) {
+ case HW_DMA_CAP_64B:
+ case HW_DMA_CAP_PTP:
+ desc_idx <<= 1;
+ break;
+ case HW_DMA_CAP_64B_PTP:
+ desc_idx *= 3;
+ break;
+ default:
+ break;
+ }
+ return desc_idx;
#endif
- return idx;
+ return desc_idx;
}
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
static struct macb_dma_desc_64 *macb_64b_desc(struct macb *bp, struct macb_dma_desc *desc)
{
- return (struct macb_dma_desc_64 *)((void *)desc + sizeof(struct macb_dma_desc));
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
+ return (struct macb_dma_desc_64 *)((void *)desc + sizeof(struct macb_dma_desc));
+ return NULL;
}
#endif
@@ -600,7 +651,7 @@ static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, dma_addr_
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
struct macb_dma_desc_64 *desc_64;
- if (bp->hw_dma_cap == HW_DMA_CAP_64B) {
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
desc_64 = macb_64b_desc(bp, desc);
desc_64->addrh = upper_32_bits(addr);
}
@@ -614,7 +665,7 @@ static dma_addr_t macb_get_addr(struct macb *bp, struct macb_dma_desc *desc)
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
struct macb_dma_desc_64 *desc_64;
- if (bp->hw_dma_cap == HW_DMA_CAP_64B) {
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
desc_64 = macb_64b_desc(bp, desc);
addr = ((u64)(desc_64->addrh) << 32);
}
@@ -713,7 +764,7 @@ static void macb_tx_error_task(struct work_struct *work)
/* Reinitialize the TX desc queue */
queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma));
#endif
/* Make TX ring reflect state of hardware */
@@ -1921,9 +1972,13 @@ static void macb_configure_dma(struct macb *bp)
dmacfg &= ~GEM_BIT(TXCOEN);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
dmacfg |= GEM_BIT(ADDR64);
#endif
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ if (bp->hw_dma_cap & HW_DMA_CAP_PTP)
+ dmacfg |= GEM_BIT(RXEXT) | GEM_BIT(TXEXT);
+#endif
netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n",
dmacfg);
gem_writel(bp, DMACFG, dmacfg);
@@ -1971,14 +2026,15 @@ static void macb_init_hw(struct macb *bp)
/* Initialize TX and RX buffers */
macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma));
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
macb_writel(bp, RBQPH, upper_32_bits(bp->rx_ring_dma));
#endif
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
- queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma));
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
+ queue_writel(queue, TBQPH,
+ upper_32_bits(queue->tx_ring_dma));
#endif
/* Enable interrupts */
@@ -2579,6 +2635,18 @@ static void macb_configure_caps(struct macb *bp,
dcfg = gem_readl(bp, DCFG2);
if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0)
bp->caps |= MACB_CAPS_FIFO_MODE;
+ /* if HWSTAMP is configure and gem has the capability */
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ bp->ptp_hw_support = false;
+ if (gem_has_ptp(bp)) {
+ if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5)))
+ pr_err("GEM doesn't support hardware ptp.\n");
+ else {
+ pr_emerg("rozieblo: ptp_hw_support = true");
+ bp->ptp_hw_support = true;
+ }
+ }
+#endif
}
dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps);
@@ -2716,7 +2784,7 @@ static int macb_init(struct platform_device *pdev)
queue->IMR = GEM_IMR(hw_q - 1);
queue->TBQP = GEM_TBQP(hw_q - 1);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
queue->TBQPH = GEM_TBQPH(hw_q - 1);
#endif
} else {
@@ -2727,7 +2795,7 @@ static int macb_init(struct platform_device *pdev)
queue->IMR = MACB_IMR;
queue->TBQP = MACB_TBQP;
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap == HW_DMA_CAP_64B)
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
queue->TBQPH = MACB_TBQPH;
#endif
}
@@ -3307,19 +3375,24 @@ static int macb_probe(struct platform_device *pdev)
bp->wol |= MACB_WOL_HAS_MAGIC_PACKET;
device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET);
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) {
- dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
- bp->hw_dma_cap = HW_DMA_CAP_64B;
- } else
- bp->hw_dma_cap = HW_DMA_CAP_32B;
-#endif
-
spin_lock_init(&bp->lock);
/* setup capabilities */
macb_configure_caps(bp, macb_config);
+#ifdef MACB_EXT_DESC
+ bp->hw_dma_cap = HW_DMA_CAP_32B;
+#endif
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) {
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
+ bp->hw_dma_cap |= HW_DMA_CAP_64B;
+ }
+#endif
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ if (bp->ptp_hw_support)
+ bp->hw_dma_cap |= HW_DMA_CAP_PTP;
+#endif
platform_set_drvdata(pdev, dev);
dev->irq = platform_get_irq(pdev, 0);
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index ec037b0..2606970 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -12,6 +12,10 @@
#include <linux/phy.h>
+#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP)
+#define MACB_EXT_DESC
+#endif
+
#define MACB_GREGS_NBR 16
#define MACB_GREGS_VERSION 2
#define MACB_MAX_QUEUES 8
@@ -269,6 +273,10 @@
#define GEM_RXBS_SIZE 8
#define GEM_DDRP_OFFSET 24 /* disc_when_no_ahb */
#define GEM_DDRP_SIZE 1
+#define GEM_RXEXT_OFFSET 28 /* RX extended Buffer Descriptor mode */
+#define GEM_RXEXT_SIZE 1
+#define GEM_TXEXT_OFFSET 29 /* TX extended Buffer Descriptor mode */
+#define GEM_TXEXT_SIZE 1
#define GEM_ADDR64_OFFSET 30 /* Address bus width - 64b or 32b */
#define GEM_ADDR64_SIZE 1
@@ -425,6 +433,11 @@
#define GEM_TX_PKT_BUFF_OFFSET 21
#define GEM_TX_PKT_BUFF_SIZE 1
+
+/* Bitfields in DCFG5. */
+#define GEM_TSU_OFFSET 8
+#define GEM_TSU_SIZE 1
+
/* Bitfields in DCFG6. */
#define GEM_PBUF_LSO_OFFSET 27
#define GEM_PBUF_LSO_SIZE 1
@@ -546,16 +559,21 @@ struct macb_dma_desc {
u32 ctrl;
};
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
-enum macb_hw_dma_cap {
- HW_DMA_CAP_32B,
- HW_DMA_CAP_64B,
-};
+#ifdef MACB_EXT_DESC
+#define HW_DMA_CAP_32B 0
+#define HW_DMA_CAP_64B (1 << 0)
+#define HW_DMA_CAP_PTP (1 << 1)
+#define HW_DMA_CAP_64B_PTP (HW_DMA_CAP_64B | HW_DMA_CAP_PTP)
struct macb_dma_desc_64 {
u32 addrh;
u32 resvd;
};
+
+struct macb_dma_desc_ptp {
+ u32 ts_1;
+ u32 ts_2;
+};
#endif
/* DMA descriptor bitfields */
@@ -954,8 +972,12 @@ struct macb {
u32 wol;
struct macb_ptp_info *ptp_info; /* macb-ptp interface */
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- enum macb_hw_dma_cap hw_dma_cap;
+#ifdef MACB_EXT_DESC
+ uint8_t hw_dma_cap;
+#endif
+
+#ifdef CONFIG_MACB_USE_HWSTAMP
+ bool ptp_hw_support;
#endif
};
--
2.4.5
^ permalink raw reply related
* [PATCH 8/8] ARM: dts: imx7d-sdb: Enable PCIe peripheral
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky, Sascha Hauer, Fabio Estevam,
Rob Herring, Mark Rutland, Russell King, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170413133242.5068-1-andrew.smirnov@gmail.com>
Enable PCIe peripheral on this board.
Cc: yurovsky@gmail.com
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/boot/dts/imx7d-sdb.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts
index e0ff276..f77e26a 100644
--- a/arch/arm/boot/dts/imx7d-sdb.dts
+++ b/arch/arm/boot/dts/imx7d-sdb.dts
@@ -352,6 +352,13 @@
};
};
+&pcie {
+ pinctrl-names = "default";
+ reset-gpio = <&gpio_spi 1 GPIO_ACTIVE_LOW>;
+ disable-gpio = <&gpio_spi 0 GPIO_ACTIVE_LOW>;
+ status = "okay";
+};
+
&pwm1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm1>;
--
2.9.3
^ permalink raw reply related
* [PATCH 7/8] ARM: dts: imx7d: Add node for PCIe controller
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky, Sascha Hauer, Fabio Estevam,
Rob Herring, Mark Rutland, Russell King, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170413133242.5068-1-andrew.smirnov@gmail.com>
Cc: yurovsky@gmail.com
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/boot/dts/imx7d.dtsi | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm/boot/dts/imx7d.dtsi b/arch/arm/boot/dts/imx7d.dtsi
index f6dee41..bbe23e4 100644
--- a/arch/arm/boot/dts/imx7d.dtsi
+++ b/arch/arm/boot/dts/imx7d.dtsi
@@ -42,6 +42,7 @@
*/
#include "imx7s.dtsi"
+#include <dt-bindings/reset/imx7-reset.h>
/ {
cpus {
@@ -127,6 +128,43 @@
fsl,num-rx-queues=<3>;
status = "disabled";
};
+
+ pcie: pcie@0x33800000 {
+ compatible = "fsl,imx7d-pcie", "snps,dw-pcie";
+ reg = <0x33800000 0x4000>,
+ <0x4ff00000 0x80000>;
+ reg-names = "dbi", "config";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ bus-range = <0x00 0xff>;
+ ranges = <0x81000000 0 0 0x4ff80000 0 0x00010000 /* downstream I/O */
+ 0x82000000 0 0x40000000 0x40000000 0 0x0ff00000>; /* non-prefetchable memory */
+ num-lanes = <1>;
+ interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_PCIE_CTRL_ROOT_CLK>,
+ <&clks IMX7D_PLL_ENET_MAIN_100M_CLK>,
+ <&clks IMX7D_PCIE_PHY_ROOT_CLK>;
+ clock-names = "pcie", "pcie_bus", "pcie_phy";
+ assigned-clocks = <&clks IMX7D_PCIE_CTRL_ROOT_SRC>,
+ <&clks IMX7D_PCIE_PHY_ROOT_SRC>;
+ assigned-clock-parents = <&clks IMX7D_PLL_ENET_MAIN_250M_CLK>,
+ <&clks IMX7D_PLL_ENET_MAIN_100M_CLK>;
+
+ fsl,max-link-speed = <2>;
+ power-domains = <&pgc_pcie_phy>;
+ resets = <&src IMX7_RESET_PCIEPHY>,
+ <&src IMX7_RESET_PCIE_CTRL_APPS_EN>;
+ reset-names = "pciephy", "apps";
+ status = "disabled";
+ };
};
&ca_funnel_ports {
--
2.9.3
^ permalink raw reply related
* [PATCH 6/8] ARM: dts: imx7d-sdb: Add GPIO expander node
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky, Sascha Hauer, Fabio Estevam,
Rob Herring, Mark Rutland, Russell King, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170413133242.5068-1-andrew.smirnov@gmail.com>
Add node for U38, a 74LV595PW serial-in shift register that acts as a
GPIO expander on the board.
Cc: yurovsky@gmail.com
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/boot/dts/imx7d-sdb.dts | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts
index 5be01a1..e0ff276 100644
--- a/arch/arm/boot/dts/imx7d-sdb.dts
+++ b/arch/arm/boot/dts/imx7d-sdb.dts
@@ -52,6 +52,30 @@
reg = <0x80000000 0x80000000>;
};
+ spi4 {
+ compatible = "spi-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi1>;
+ status = "okay";
+ gpio-sck = <&gpio1 13 0>;
+ gpio-mosi = <&gpio1 9 0>;
+ cs-gpios = <&gpio1 12 0>;
+ num-chipselects = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ gpio_spi: gpio_spi@0 {
+ compatible = "fairchild,74hc595";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0>;
+ registers-number = <1>;
+ /* Enable PERI_3V3, SENSOR_RST_B and HDMI_RST*/
+ registers-default = /bits/ 8 <0x74>;
+ spi-max-frequency = <100000>;
+ };
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
@@ -642,5 +666,13 @@
fsl,pins = <
MX7D_PAD_LPSR_GPIO1_IO01__PWM1_OUT 0x110b0
>;
+
+ pinctrl_spi1: spi1grp {
+ fsl,pins = <
+ MX7D_PAD_GPIO1_IO09__GPIO1_IO9 0x59
+ MX7D_PAD_GPIO1_IO12__GPIO1_IO12 0x59
+ MX7D_PAD_GPIO1_IO13__GPIO1_IO13 0x59
+ >;
+ };
};
};
--
2.9.3
^ permalink raw reply related
* [PATCH 5/8] ARM: dts: imx7s: Mark 'gpr' compatible with i.MX6 variant
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Sascha Hauer,
Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170413133242.5068-1-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
List GPR block as compatible "fsl,imx6q-iomuxc-gpr" to support drivers
requesting it that way (PCIe driver is one example).
Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
arch/arm/boot/dts/imx7s.dtsi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 1a7058f..cc23478 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -491,7 +491,8 @@
};
gpr: iomuxc-gpr@30340000 {
- compatible = "fsl,imx7d-iomuxc-gpr", "syscon";
+ compatible = "fsl,imx7d-iomuxc-gpr",
+ "fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x30340000 0x10000>;
};
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 4/8] ARM: dts: imx7s: Add node for GPC
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky, Sascha Hauer, Fabio Estevam,
Rob Herring, Mark Rutland, Russell King, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170413133242.5068-1-andrew.smirnov@gmail.com>
Add node for GPC and specify as a parent interrupt controller for SoC bus.
Cc: yurovsky@gmail.com
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/boot/dts/imx7s.dtsi | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 8fee299..1a7058f 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -42,6 +42,7 @@
*/
#include <dt-bindings/clock/imx7d-clock.h>
+#include <dt-bindings/power/imx7-power.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -119,7 +120,7 @@
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
- interrupt-parent = <&intc>;
+ interrupt-parent = <&gpc>;
ranges;
funnel@30041000 {
@@ -301,6 +302,7 @@
interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
#interrupt-cells = <3>;
interrupt-controller;
+ interrupt-parent = <&intc>;
reg = <0x31001000 0x1000>,
<0x31002000 0x2000>,
<0x31004000 0x2000>,
@@ -309,6 +311,7 @@
timer {
compatible = "arm,armv7-timer";
+ interrupt-parent = <&intc>;
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
@@ -564,6 +567,28 @@
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
#reset-cells = <1>;
};
+
+ gpc: gpc@303a0000 {
+ compatible = "fsl,imx7d-gpc";
+ reg = <0x303a0000 0x10000>;
+ interrupt-controller;
+ interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ #power-domain-cells = <1>;
+
+ pgc {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pgc_pcie_phy: pgc-pcie-phy-domain {
+ #power-domain-cells = <0>;
+
+ reg = <IMX7_POWER_DOMAIN_PCIE_PHY>;
+ power-supply = <®_1p0d>;
+ };
+ };
+ };
};
aips2: aips-bus@30400000 {
--
2.9.3
^ permalink raw reply related
* [PATCH 3/8] ARM: dts: imx7s: Adjust anatop-enable-bit for 'reg_1p0d'
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Sascha Hauer,
Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170413133242.5068-1-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
In PMU_REG_1P0Dn ENABLE_LINREG is bit 0. Bit 31 is called OVERRIDE and
it serves the function of granting permission to GPC IP block to alter
various bit-fields of the register. The reason why this property, that
trickeld here from Freescale BSP, is set to 31 is because in the code
it came from it is used in conjunction with a notifier handler for
REGULATOR_EVENT_PRE_DO_ENABLE and REGULATOR_EVENT_PRE_DO_DISABLE
events (not found in upstream kernel) that triggers GPC to start
manipulating aforementioned other bitfields.
Since:
a) none of the aforementioned machinery is implemented by
upstream
b) using 'anatop-enable-bit' in that capacity is a bit of a
semantic stretch
simplify the situation by setting the value of 'anatop-enable-bit' to
point to ENABLE_LINREG (same as i.MX6).
Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
arch/arm/boot/dts/imx7s.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 22c9788..8fee299 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -516,7 +516,7 @@
anatop-min-bit-val = <8>;
anatop-min-voltage = <800000>;
anatop-max-voltage = <1200000>;
- anatop-enable-bit = <31>;
+ anatop-enable-bit = <0>;
};
};
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 2/8] ARM: dts: imx6: Specify 'anatop-enable-bit' where appropriate
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Mark Rutland, devicetree, Andrey Smirnov, Russell King,
linux-kernel, Rob Herring, Sascha Hauer, Fabio Estevam,
linux-arm-kernel, yurovsky
In-Reply-To: <20170413133242.5068-1-andrew.smirnov@gmail.com>
ENABLE_LINREG bit is implemented by 3P0, 1P1 and 2P5 regulators on
i.MX6. This property is present in similar code in Fresscale BSP and
made its way upstream in imx6ul.dtsi, so this patch adds this property
to the rest of i.MX6 family for completness.
Cc: yurovsky@gmail.com
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/boot/dts/imx6qdl.dtsi | 3 +++
arch/arm/boot/dts/imx6sl.dtsi | 3 +++
arch/arm/boot/dts/imx6sx.dtsi | 3 +++
3 files changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index e426faa..0576ef6 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -644,6 +644,7 @@
anatop-min-bit-val = <4>;
anatop-min-voltage = <800000>;
anatop-max-voltage = <1375000>;
+ anatop-enable-bit = <0>;
};
regulator-3p0 {
@@ -658,6 +659,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2625000>;
anatop-max-voltage = <3400000>;
+ anatop-enable-bit = <0>;
};
regulator-2p5 {
@@ -672,6 +674,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2100000>;
anatop-max-voltage = <2875000>;
+ anatop-enable-bit = <0>;
};
reg_arm: regulator-vddcore {
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index cc9572e..3243af4 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -530,6 +530,7 @@
anatop-min-bit-val = <4>;
anatop-min-voltage = <800000>;
anatop-max-voltage = <1375000>;
+ anatop-enable-bit = <0>;
};
regulator-3p0 {
@@ -544,6 +545,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2625000>;
anatop-max-voltage = <3400000>;
+ anatop-enable-bit = <0>;
};
regulator-2p5 {
@@ -558,6 +560,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2100000>;
anatop-max-voltage = <2850000>;
+ anatop-enable-bit = <0>;
};
reg_arm: regulator-vddcore {
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index 3f1416b..f16b9df 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -587,6 +587,7 @@
anatop-min-bit-val = <4>;
anatop-min-voltage = <800000>;
anatop-max-voltage = <1375000>;
+ anatop-enable-bit = <0>;
};
regulator-3p0 {
@@ -601,6 +602,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2625000>;
anatop-max-voltage = <3400000>;
+ anatop-enable-bit = <0>;
};
regulator-2p5 {
@@ -615,6 +617,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2100000>;
anatop-max-voltage = <2875000>;
+ anatop-enable-bit = <0>;
};
reg_arm: regulator-vddcore {
--
2.9.3
^ permalink raw reply related
* [PATCH 1/8] Revert "ARM: dts: imx: Remove unexistant property"
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Sascha Hauer,
Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170413133242.5068-1-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Commit ca7734a ("regulator: anatop: Add support for
"anatop-enable-bit"") added code to support this particular binding
and 'anatop-enable-bit' is no longer an unused property.
This reverts commit 27958ccdf29e9971732e02494b48be54b0691269.
Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
arch/arm/boot/dts/imx6ul.dtsi | 1 +
arch/arm/boot/dts/imx7s.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi
index b9d7d2d..6da2b77 100644
--- a/arch/arm/boot/dts/imx6ul.dtsi
+++ b/arch/arm/boot/dts/imx6ul.dtsi
@@ -542,6 +542,7 @@
anatop-min-bit-val = <0>;
anatop-min-voltage = <2625000>;
anatop-max-voltage = <3400000>;
+ anatop-enable-bit = <0>;
};
reg_arm: regulator-vddcore {
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index c4f12fd..22c9788 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -516,6 +516,7 @@
anatop-min-bit-val = <8>;
anatop-min-voltage = <800000>;
anatop-max-voltage = <1200000>;
+ anatop-enable-bit = <31>;
};
};
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 0/8] i.MX7 PCIe related device tree changes
From: Andrey Smirnov @ 2017-04-13 13:32 UTC (permalink / raw)
To: Shawn Guo
Cc: Andrey Smirnov, yurovsky, Sascha Hauer, Fabio Estevam,
Rob Herring, Mark Rutland, Russell King, devicetree, linux-kernel,
linux-arm-kernel
Shawn, everyone:
This series includes changes made to device-tree in order to support
PCIe on i.MX7 platform. They include:
- Bringing 'anatop-enable-bit' property of ANATOP regulators back
and extending it to all of the HW it is applicable to
- Adding GPCv2 node for i.MX7 (which was missing, despite the
irqchip driver for it being in the tree for quite some time)
- Adding a PCIe node for i.MX7
- Adding GPIO expander used by PCIe and enabling PCIe node from
above on i.MX7 based Sabre board
As usual, feedback is welcome.
Thanks,
Andrey Smrinov
Andrey Smirnov (8):
Revert "ARM: dts: imx: Remove unexistant property"
ARM: dts: imx6: Specify 'anatop-enable-bit' where appropriate
ARM: dts: imx7s: Adjust anatop-enable-bit for 'reg_1p0d'
ARM: dts: imx7s: Add node for GPC
ARM: dts: imx7s: Mark 'gpr' compatible with i.MX6 variant
ARM: dts: imx7d-sdb: Add GPIO expander node
ARM: dts: imx7d: Add node for PCIe controller
ARM: dts: imx7d-sdb: Enable PCIe peripheral
arch/arm/boot/dts/imx6qdl.dtsi | 3 +++
arch/arm/boot/dts/imx6sl.dtsi | 3 +++
arch/arm/boot/dts/imx6sx.dtsi | 3 +++
arch/arm/boot/dts/imx6ul.dtsi | 1 +
arch/arm/boot/dts/imx7d-sdb.dts | 39 +++++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/imx7d.dtsi | 38 ++++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/imx7s.dtsi | 31 +++++++++++++++++++++++++++++--
7 files changed, 116 insertions(+), 2 deletions(-)
--
2.9.3
^ permalink raw reply
* Re: [PATCH 1/2] mfd: arizona: Add GPIO maintain state flag
From: Charles Keepax @ 2017-04-13 13:07 UTC (permalink / raw)
To: Linus Walleij
Cc: Richard Fitzgerald, Rob Herring, Lee Jones, Alexandre Courbot,
Mark Rutland, linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
open list:WOLFSON MICROELECTRONICS DRIVERS
In-Reply-To: <CACRpkdYAgd+ovH5x+GpbqPqGNG8W=t3A_g1rpY6tha2=FPsfOA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Thu, Apr 13, 2017 at 02:48:45PM +0200, Linus Walleij wrote:
> On Thu, Apr 13, 2017 at 2:21 PM, Richard Fitzgerald
> <rf-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> wrote:
> > On Thu, 2017-04-13 at 14:14 +0200, Linus Walleij wrote:
> >> On Thu, Apr 13, 2017 at 11:15 AM, Charles Keepax
> >> <ckeepax-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> wrote:
> >> > On Tue, Apr 11, 2017 at 10:34:27AM +0100, Richard Fitzgerald wrote:
> >>
> >> >> 3) The codec only has to be kept awake while any such GPIO is actually
> >> >> in use. See (2)
> >> >
> >> > Yeah option 3 is the primary issue here, we only want to keep the
> >> > CODEC enabled whilst specific GPIOs are in use. As GPIOs can be
> >> > dynamically requested/released by things in the kernel we want to
> >> > know which GPIOs require the CODEC to be kept alive. Also in the
> >> > future one might be tempted to add maintain whilst high and
> >> > maintain whilst low options for lines with pulls on them to
> >> > further optimise power.
> >>
> >> Why does this have to be encoded as information in the device
> >> tree? Isn't it better to use a static table in the driver?
> >>
> >> I don't see what use system integrators and others playing
> >> around with the device tree has of this, it will just be confusing
> >> to them if it is a chip-internal detail.
> >>
> >
> > They are GPIOs for connecting to external hardware, we don't know what
> > people are going to connect them to so they have to tell us how they
> > need them to behave.
>
> Aha it is a consumer configuration thing, then I see it.
>
> I think it seems a bit odd that it is assumed that the default is that
> we should *not* preserve the GPIO output value if we go to sleep.
> Should the flag be inverted?
>
I agree that is a bit odd, my thinking was keeping the behaviour
the same for existing systems. But it only introduces a power
regression perhaps it is ok to require people to update their DT
to avoid that?
> Also, why can't we just use a generic flag for this, it seems very
> very generic.
>
> Look in:
> include/dt-bindings/gpio/gpio.h
>
> Is there any reasons why we can't have:
> /* Bit 3 express GPIO suspend/resume persistance in low power mode */
> #define GPIO_MUST_KEEP_VALUE 0
> #define GPIO_MAY_LOOSE_VALUE_DURING_SLEEP 8
>
> Yeah it's talkative but informative. This way you can mark up lines
> that are OK to loose their value during low-power/sleep using
> just (new) standard bindings that can be reused by others,
> also optionally making it possible for the gpiolib core to take action
> on these properties if need be.
>
I certainly have no objections to making this a core feature if
you are comfortable with that. I will have a look at what that
would look like.
Thanks,
Charles
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH RFC 0/5] *** SPI Slave mode support ***
From: Mark Brown @ 2017-04-13 12:59 UTC (permalink / raw)
To: jiada_wang-nmGgyN9QBj3QT0dZR+AlfA
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
fabio.estevam-3arQi8VN3Tc, linux-spi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Geert Uytterhoeven
In-Reply-To: <1492085644-4195-1-git-send-email-jiada_wang-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1740 bytes --]
On Thu, Apr 13, 2017 at 05:13:59AM -0700, jiada_wang-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org wrote:
> From: Jiada Wang <jiada_wang-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
>
> v1:
> add Slave mode support in SPI core
> spidev create slave device when SPI controller work in slave mode
> spi-imx support to work in slave mode
Adding Geert who also had a series doing this in progress that was
getting very near to being merged.
>
> Jiada Wang (5):
> spi: core: add support to work in Slave mode
> spi: spidev: use different name for SPI controller slave mode device
> spi: imx: add selection for iMX53 and iMX6 controller
> ARM: dts: imx: change compatiblity for SPI controllers on imx53 later
> soc
> spi: imx: Add support for SPI Slave mode for imx53 and imx6 chips
>
> Documentation/devicetree/bindings/spi/spi-bus.txt | 27 ++-
> Documentation/spi/spi-summary | 19 +-
> arch/arm/boot/dts/imx53.dtsi | 4 +-
> arch/arm/boot/dts/imx6q.dtsi | 2 +-
> arch/arm/boot/dts/imx6qdl.dtsi | 8 +-
> arch/arm/boot/dts/imx6sl.dtsi | 8 +-
> arch/arm/boot/dts/imx6sx.dtsi | 8 +-
> arch/arm/boot/dts/imx6ul.dtsi | 8 +-
> drivers/spi/Kconfig | 14 +-
> drivers/spi/spi-imx.c | 216 ++++++++++++++++++++--
> drivers/spi/spi.c | 23 ++-
> drivers/spi/spidev.c | 15 +-
> include/linux/spi/spi.h | 15 ++
> 13 files changed, 310 insertions(+), 57 deletions(-)
>
> --
> 2.7.4
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] mfd: arizona: Add GPIO maintain state flag
From: Linus Walleij @ 2017-04-13 12:48 UTC (permalink / raw)
To: Richard Fitzgerald
Cc: Charles Keepax, Rob Herring, Lee Jones, Alexandre Courbot,
Mark Rutland, linux-gpio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
open list:WOLFSON MICROELECTRONICS DRIVERS
In-Reply-To: <1492086093.4826.25.camel@rf-debian.wolfsonmicro.main>
On Thu, Apr 13, 2017 at 2:21 PM, Richard Fitzgerald
<rf@opensource.wolfsonmicro.com> wrote:
> On Thu, 2017-04-13 at 14:14 +0200, Linus Walleij wrote:
>> On Thu, Apr 13, 2017 at 11:15 AM, Charles Keepax
>> <ckeepax@opensource.wolfsonmicro.com> wrote:
>> > On Tue, Apr 11, 2017 at 10:34:27AM +0100, Richard Fitzgerald wrote:
>>
>> >> 3) The codec only has to be kept awake while any such GPIO is actually
>> >> in use. See (2)
>> >
>> > Yeah option 3 is the primary issue here, we only want to keep the
>> > CODEC enabled whilst specific GPIOs are in use. As GPIOs can be
>> > dynamically requested/released by things in the kernel we want to
>> > know which GPIOs require the CODEC to be kept alive. Also in the
>> > future one might be tempted to add maintain whilst high and
>> > maintain whilst low options for lines with pulls on them to
>> > further optimise power.
>>
>> Why does this have to be encoded as information in the device
>> tree? Isn't it better to use a static table in the driver?
>>
>> I don't see what use system integrators and others playing
>> around with the device tree has of this, it will just be confusing
>> to them if it is a chip-internal detail.
>>
>
> They are GPIOs for connecting to external hardware, we don't know what
> people are going to connect them to so they have to tell us how they
> need them to behave.
Aha it is a consumer configuration thing, then I see it.
I think it seems a bit odd that it is assumed that the default is that
we should *not* preserve the GPIO output value if we go to sleep.
Should the flag be inverted?
Also, why can't we just use a generic flag for this, it seems very
very generic.
Look in:
include/dt-bindings/gpio/gpio.h
Is there any reasons why we can't have:
/* Bit 3 express GPIO suspend/resume persistance in low power mode */
#define GPIO_MUST_KEEP_VALUE 0
#define GPIO_MAY_LOOSE_VALUE_DURING_SLEEP 8
Yeah it's talkative but informative. This way you can mark up lines
that are OK to loose their value during low-power/sleep using
just (new) standard bindings that can be reused by others,
also optionally making it possible for the gpiolib core to take action
on these properties if need be.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH 1/2] mfd: arizona: Add GPIO maintain state flag
From: Richard Fitzgerald @ 2017-04-13 12:21 UTC (permalink / raw)
To: Linus Walleij
Cc: Charles Keepax, Rob Herring, Lee Jones, Alexandre Courbot,
Mark Rutland, linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
open list:WOLFSON MICROELECTRONICS DRIVERS
In-Reply-To: <CACRpkdZ6PBv72BCn1bXsvhcHD2+dsCdh=3F3Vnnm7AWZqyWrig-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Thu, 2017-04-13 at 14:14 +0200, Linus Walleij wrote:
> On Thu, Apr 13, 2017 at 11:15 AM, Charles Keepax
> <ckeepax-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> wrote:
> > On Tue, Apr 11, 2017 at 10:34:27AM +0100, Richard Fitzgerald wrote:
>
> >> 3) The codec only has to be kept awake while any such GPIO is actually
> >> in use. See (2)
> >
> > Yeah option 3 is the primary issue here, we only want to keep the
> > CODEC enabled whilst specific GPIOs are in use. As GPIOs can be
> > dynamically requested/released by things in the kernel we want to
> > know which GPIOs require the CODEC to be kept alive. Also in the
> > future one might be tempted to add maintain whilst high and
> > maintain whilst low options for lines with pulls on them to
> > further optimise power.
>
> Why does this have to be encoded as information in the device
> tree? Isn't it better to use a static table in the driver?
>
> I don't see what use system integrators and others playing
> around with the device tree has of this, it will just be confusing
> to them if it is a chip-internal detail.
>
They are GPIOs for connecting to external hardware, we don't know what
people are going to connect them to so they have to tell us how they
need them to behave.
> Yours,
> Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] mfd: arizona: Add GPIO maintain state flag
From: Linus Walleij @ 2017-04-13 12:14 UTC (permalink / raw)
To: Charles Keepax
Cc: Richard Fitzgerald, Rob Herring, Lee Jones, Alexandre Courbot,
Mark Rutland, linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
open list:WOLFSON MICROELECTRONICS DRIVERS
In-Reply-To: <20170413091513.GA1878-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
On Thu, Apr 13, 2017 at 11:15 AM, Charles Keepax
<ckeepax-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org> wrote:
> On Tue, Apr 11, 2017 at 10:34:27AM +0100, Richard Fitzgerald wrote:
>> 3) The codec only has to be kept awake while any such GPIO is actually
>> in use. See (2)
>
> Yeah option 3 is the primary issue here, we only want to keep the
> CODEC enabled whilst specific GPIOs are in use. As GPIOs can be
> dynamically requested/released by things in the kernel we want to
> know which GPIOs require the CODEC to be kept alive. Also in the
> future one might be tempted to add maintain whilst high and
> maintain whilst low options for lines with pulls on them to
> further optimise power.
Why does this have to be encoded as information in the device
tree? Isn't it better to use a static table in the driver?
I don't see what use system integrators and others playing
around with the device tree has of this, it will just be confusing
to them if it is a chip-internal detail.
Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH RFC 5/5] spi: imx: Add support for SPI Slave mode for imx53 and imx6 chips
From: jiada_wang @ 2017-04-13 12:14 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland, shawnguo, kernel, fabio.estevam
Cc: linux-spi, devicetree, linux-kernel, linux-arm-kernel, Jiada Wang
In-Reply-To: <1492085644-4195-1-git-send-email-jiada_wang@mentor.com>
From: Jiada Wang <jiada_wang@mentor.com>
Previously i.MX SPI controller only works in Master mode.
This patch adds support to work also in Slave mode.
Currently SPI Slave mode support patch has the following limitations:
1. The stale data in RXFIFO will be dropped when the Slave does any new
transfer.
2. One transfer can be finished only after all transfer->len data been
transferred to master device
3. Slave device only accepts transfer->len data. Any data longer than this
from master device will be dropped. Any data shorter than this from
master will cause SPI to stuck due to mentioned HW limitation 2.
4. Only PIO transfer is supported in Slave mode.
Following HW limitation applies:
1. ECSPI has a HW issue when works in Slave mode, after 64
words written to TXFIFO, even TXFIFO becomes empty,
ECSPI_TXDATA keeps shift out the last word data,
so we have to disable ECSPI when in slave mode after the
transfer completes
2. Due to Freescale errata ERR003775 "eCSPI: Burst completion by Chip
Select (SS) signal in Slave mode is not functional" burst size must
be set exactly to the size of the transfer. This limit SPI transaction
with maximum 2^12 bits.
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/spi/spi-imx.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 159 insertions(+), 14 deletions(-)
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index b2323b9..f6e1baa 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -53,9 +53,13 @@
/* generic defines to abstract from the different register layouts */
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
+#define MXC_INT_RDR BIT(4) /* Receive date threshold interrupt */
/* The maximum bytes that a sdma BD can transfer.*/
#define MAX_SDMA_BD_BYTES (1 << 15)
+/* The maximum bytes that IMX53_ECSPI can transfer in slave mode.*/
+#define MX53_MAX_TRANSFER_BYTES 512
+
struct spi_imx_config {
unsigned int speed_hz;
unsigned int bpw;
@@ -79,6 +83,7 @@ struct spi_imx_devtype_data {
void (*trigger)(struct spi_imx_data *);
int (*rx_available)(struct spi_imx_data *);
void (*reset)(struct spi_imx_data *);
+ void (*disable)(struct spi_imx_data *);
enum spi_imx_devtype devtype;
};
@@ -104,6 +109,10 @@ struct spi_imx_data {
const void *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
+ /* Slave mode */
+ unsigned int slave_mode;
+ unsigned int slave_burst;
+
/* DMA */
bool usedma;
u32 wml;
@@ -156,6 +165,19 @@ static inline bool spi_imx_has_dmamode(struct spi_imx_data *d)
}
}
+static bool spi_imx_has_slavemode(struct spi_master *master)
+{
+ struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
+
+ switch (spi_imx->devtype_data->devtype) {
+ case IMX51_ECSPI:
+ case IMX53_ECSPI:
+ return true;
+ default:
+ return false;
+ }
+}
+
#define MXC_SPI_BUF_RX(type) \
static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \
{ \
@@ -285,6 +307,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
#define MX51_ECSPI_INT 0x10
#define MX51_ECSPI_INT_TEEN (1 << 0)
#define MX51_ECSPI_INT_RREN (1 << 3)
+#define MX51_ECSPI_INT_RDREN (1 << 4)
#define MX51_ECSPI_DMA 0x14
#define MX51_ECSPI_DMA_TX_WML(wml) ((wml) & 0x3f)
@@ -301,6 +324,51 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
#define MX51_ECSPI_TESTREG 0x20
#define MX51_ECSPI_TESTREG_LBC BIT(31)
+static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx)
+{
+ u32 val = be32_to_cpu(readl(spi_imx->base + MXC_CSPIRXDATA));
+
+ if (spi_imx->rx_buf) {
+ int shift = spi_imx->slave_burst % sizeof(val);
+
+ if (shift) {
+ memcpy(spi_imx->rx_buf,
+ ((u8 *)&val) + sizeof(val) - shift, shift);
+ } else {
+ *((u32 *)spi_imx->rx_buf) = val;
+ shift = sizeof(val);
+ }
+
+ spi_imx->rx_buf += shift;
+ spi_imx->slave_burst -= shift;
+ }
+}
+
+static void mx53_ecspi_tx_slave(struct spi_imx_data *spi_imx)
+{
+ u32 val = 0;
+ int shift = spi_imx->count % sizeof(val);
+
+ if (spi_imx->tx_buf) {
+ if (shift) {
+ memcpy(((u8 *)&val) + sizeof(val) - shift,
+ spi_imx->tx_buf, shift);
+ } else {
+ val = *((u32 *)spi_imx->tx_buf);
+ shift = sizeof(val);
+ }
+ val = cpu_to_be32(val);
+ spi_imx->tx_buf += shift;
+ }
+
+ if (!shift)
+ shift = sizeof(val);
+
+ spi_imx->count -= shift;
+
+ writel(val, spi_imx->base + MXC_CSPITXDATA);
+}
+
/* MX51 eCSPI */
static unsigned int mx51_ecspi_clkdiv(struct spi_imx_data *spi_imx,
unsigned int fspi, unsigned int *fres)
@@ -350,6 +418,9 @@ static void mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int enable)
if (enable & MXC_INT_RR)
val |= MX51_ECSPI_INT_RREN;
+ if (enable & MXC_INT_RDR)
+ val |= MX51_ECSPI_INT_RDREN;
+
writel(val, spi_imx->base + MX51_ECSPI_INT);
}
@@ -362,6 +433,15 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
}
+static void __maybe_unused mx51_ecspi_disable(struct spi_imx_data *spi_imx)
+{
+ u32 ctrl;
+
+ ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
+ ctrl &= ~MX51_ECSPI_CTRL_ENABLE;
+ writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
+}
+
static int mx51_ecspi_config(struct spi_device *spi,
struct spi_imx_config *config)
{
@@ -370,14 +450,13 @@ static int mx51_ecspi_config(struct spi_device *spi,
u32 clk = config->speed_hz, delay, reg;
u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
- /*
- * The hardware seems to have a race condition when changing modes. The
- * current assumption is that the selection of the channel arrives
- * earlier in the hardware than the mode bits when they are written at
- * the same time.
- * So set master mode for all channels as we do not support slave mode.
- */
- ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
+ writel(0, spi_imx->base + MX51_ECSPI_CTRL);
+
+ /* set Master or Slave mode */
+ if (spi_imx->slave_mode)
+ ctrl &= ~MX51_ECSPI_CTRL_MODE_MASK;
+ else
+ ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
/* set clock speed */
ctrl |= mx51_ecspi_clkdiv(spi_imx, config->speed_hz, &clk);
@@ -386,9 +465,21 @@ static int mx51_ecspi_config(struct spi_device *spi,
/* set chip select to use */
ctrl |= MX51_ECSPI_CTRL_CS(spi->chip_select);
- ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
+ if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx))
+ ctrl |= (spi_imx->slave_burst * 8 - 1)
+ << MX51_ECSPI_CTRL_BL_OFFSET;
+ else
+ ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
- cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select);
+ /*
+ * eCSPI burst completion by Chip Select signal in Slave mode
+ * is not functional, config SPI burst completed when
+ * BURST_LENGTH + 1 bits are received
+ */
+ if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx))
+ cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select);
+ else
+ cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select);
if (spi->mode & SPI_CPHA)
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(spi->chip_select);
@@ -767,6 +858,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
.trigger = mx51_ecspi_trigger,
.rx_available = mx51_ecspi_rx_available,
.reset = mx51_ecspi_reset,
+ .disable = mx51_ecspi_disable,
.devtype = IMX51_ECSPI,
};
@@ -776,6 +868,7 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
.trigger = mx51_ecspi_trigger,
.rx_available = mx51_ecspi_rx_available,
.reset = mx51_ecspi_reset,
+ .disable = mx51_ecspi_disable,
.devtype = IMX53_ECSPI,
};
@@ -838,14 +931,16 @@ static void spi_imx_push(struct spi_imx_data *spi_imx)
spi_imx->txfifo++;
}
- spi_imx->devtype_data->trigger(spi_imx);
+ if (!spi_imx->slave_mode)
+ spi_imx->devtype_data->trigger(spi_imx);
}
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
struct spi_imx_data *spi_imx = dev_id;
- while (spi_imx->devtype_data->rx_available(spi_imx)) {
+ while (spi_imx->txfifo &&
+ spi_imx->devtype_data->rx_available(spi_imx)) {
spi_imx->rx(spi_imx);
spi_imx->txfifo--;
}
@@ -927,6 +1022,8 @@ static int spi_imx_setupxfer(struct spi_device *spi,
config.bpw = t ? t->bits_per_word : spi->bits_per_word;
config.speed_hz = t ? t->speed_hz : spi->max_speed_hz;
+ spi_imx->slave_mode = spi->slave_mode;
+
if (!config.speed_hz)
config.speed_hz = spi->max_speed_hz;
if (!config.bpw)
@@ -944,7 +1041,8 @@ static int spi_imx_setupxfer(struct spi_device *spi,
spi_imx->tx = spi_imx_buf_tx_u32;
}
- if (spi_imx_can_dma(spi_imx->bitbang.master, spi, t))
+ if (!spi->slave_mode &&
+ spi_imx_can_dma(spi_imx->bitbang.master, spi, t))
spi_imx->usedma = 1;
else
spi_imx->usedma = 0;
@@ -956,6 +1054,12 @@ static int spi_imx_setupxfer(struct spi_device *spi,
return ret;
}
+ if (is_imx53_ecspi(spi_imx) && spi_imx->slave_mode) {
+ spi_imx->rx = mx53_ecspi_rx_slave;
+ spi_imx->tx = mx53_ecspi_tx_slave;
+ spi_imx->slave_burst = t->len;
+ }
+
spi_imx->devtype_data->config(spi, &config);
return 0;
@@ -1117,16 +1221,46 @@ static int spi_imx_pio_transfer(struct spi_device *spi,
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
unsigned long transfer_timeout;
unsigned long timeout;
+ int ret = transfer->len;
+
+ if (is_imx53_ecspi(spi_imx) && spi_imx->slave_mode &&
+ transfer->len > MX53_MAX_TRANSFER_BYTES) {
+ pr_err("Transaction too big, max size is %d bytes\n",
+ MX53_MAX_TRANSFER_BYTES);
+ return -EMSGSIZE;
+ }
spi_imx->tx_buf = transfer->tx_buf;
spi_imx->rx_buf = transfer->rx_buf;
spi_imx->count = transfer->len;
spi_imx->txfifo = 0;
+ if (spi_imx->slave_mode)
+ spi_imx->slave_burst = spi_imx->count;
+
reinit_completion(&spi_imx->xfer_done);
spi_imx_push(spi_imx);
+ if (spi_imx->slave_mode) {
+ spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE |
+ MXC_INT_RDR);
+
+ if (wait_for_completion_interruptible(&spi_imx->xfer_done) < 0)
+ ret = -EINTR;
+
+ /* ecspi has a HW issue when works in Slave mode,
+ * after 64 words writtern to TXFIFO, even TXFIFO becomes empty,
+ * ECSPI_TXDATA keeps shift out the last word data,
+ * so we have to disable ECSPI when in slave mode after the
+ * transfer completes
+ */
+ if (spi_imx->devtype_data->disable)
+ spi_imx->devtype_data->disable(spi_imx);
+
+ goto out;
+ }
+
spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE);
transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);
@@ -1139,7 +1273,8 @@ static int spi_imx_pio_transfer(struct spi_device *spi,
return -ETIMEDOUT;
}
- return transfer->len;
+out:
+ return ret;
}
static int spi_imx_transfer(struct spi_device *spi,
@@ -1147,6 +1282,10 @@ static int spi_imx_transfer(struct spi_device *spi,
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
+ /* flush rxfifo before transfer */
+ while (spi_imx->devtype_data->rx_available(spi_imx))
+ spi_imx->rx(spi_imx);
+
if (spi_imx->usedma)
return spi_imx_dma_transfer(spi_imx, transfer);
else
@@ -1155,6 +1294,11 @@ static int spi_imx_transfer(struct spi_device *spi,
static int spi_imx_setup(struct spi_device *spi)
{
+ if (spi->slave_mode) {
+ dev_dbg(&spi->dev, "%s: slave mode\n", __func__);
+ return 0;
+ }
+
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
spi->mode, spi->bits_per_word, spi->max_speed_hz);
@@ -1254,6 +1398,7 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx))
spi_imx->bitbang.master->mode_bits |= SPI_LOOP;
+ spi_imx->bitbang.master->has_slavemode = spi_imx_has_slavemode;
init_completion(&spi_imx->xfer_done);
--
2.7.4
^ permalink raw reply related
* [PATCH RFC 4/5] ARM: dts: imx: change compatiblity for SPI controllers on imx53 later soc
From: jiada_wang @ 2017-04-13 12:14 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland, shawnguo, kernel, fabio.estevam
Cc: linux-spi, devicetree, linux-kernel, linux-arm-kernel, Jiada Wang
In-Reply-To: <1492085644-4195-1-git-send-email-jiada_wang@mentor.com>
From: Jiada Wang <jiada_wang@mentor.com>
for SPI controllers on imx53 and later SoCs, there is HW issue when
work in slave mode, as new device type 'IMX53_ECSPI' has been added
for these SPI controllers which is compatible with 'fsl,imx53-ecspi'.
This patch updates DTS to make imx53 later SPI controller only be
compatibile with 'fsl,imx53-ecspi'.
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
arch/arm/boot/dts/imx53.dtsi | 4 ++--
arch/arm/boot/dts/imx6q.dtsi | 2 +-
arch/arm/boot/dts/imx6qdl.dtsi | 8 ++++----
arch/arm/boot/dts/imx6sl.dtsi | 8 ++++----
arch/arm/boot/dts/imx6sx.dtsi | 8 ++++----
arch/arm/boot/dts/imx6ul.dtsi | 8 ++++----
6 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index 2e516f4..9eeafb9 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -243,7 +243,7 @@
ecspi1: ecspi@50010000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx53-ecspi";
reg = <0x50010000 0x4000>;
interrupts = <36>;
clocks = <&clks IMX5_CLK_ECSPI1_IPG_GATE>,
@@ -662,7 +662,7 @@
ecspi2: ecspi@63fac000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx53-ecspi";
reg = <0x63fac000 0x4000>;
interrupts = <37>;
clocks = <&clks IMX5_CLK_ECSPI2_IPG_GATE>,
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index e9a5d0b..7373671 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -90,7 +90,7 @@
ecspi5: ecspi@02018000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6q-ecspi", "fsl,imx53-ecspi";
reg = <0x02018000 0x4000>;
interrupts = <0 35 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6Q_CLK_ECSPI5>,
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 6d7bf64..4a4b509 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -265,7 +265,7 @@
ecspi1: ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6q-ecspi", "fsl,imx53-ecspi";
reg = <0x02008000 0x4000>;
interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_ECSPI1>,
@@ -279,7 +279,7 @@
ecspi2: ecspi@0200c000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6q-ecspi", "fsl,imx53-ecspi";
reg = <0x0200c000 0x4000>;
interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_ECSPI2>,
@@ -293,7 +293,7 @@
ecspi3: ecspi@02010000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6q-ecspi", "fsl,imx53-ecspi";
reg = <0x02010000 0x4000>;
interrupts = <0 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_ECSPI3>,
@@ -307,7 +307,7 @@
ecspi4: ecspi@02014000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6q-ecspi", "fsl,imx53-ecspi";
reg = <0x02014000 0x4000>;
interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_ECSPI4>,
diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi
index cc9572e..1d1200c 100644
--- a/arch/arm/boot/dts/imx6sl.dtsi
+++ b/arch/arm/boot/dts/imx6sl.dtsi
@@ -168,7 +168,7 @@
ecspi1: ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sl-ecspi", "fsl,imx53-ecspi";
reg = <0x02008000 0x4000>;
interrupts = <0 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI1>,
@@ -180,7 +180,7 @@
ecspi2: ecspi@0200c000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sl-ecspi", "fsl,imx53-ecspi";
reg = <0x0200c000 0x4000>;
interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI2>,
@@ -192,7 +192,7 @@
ecspi3: ecspi@02010000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sl-ecspi", "fsl,imx53-ecspi";
reg = <0x02010000 0x4000>;
interrupts = <0 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI3>,
@@ -204,7 +204,7 @@
ecspi4: ecspi@02014000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sl-ecspi", "fsl,imx53-ecspi";
reg = <0x02014000 0x4000>;
interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_ECSPI4>,
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index dd4ec85..c115ed3 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -251,7 +251,7 @@
ecspi1: ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sx-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sx-ecspi", "fsl,imx53-ecspi";
reg = <0x02008000 0x4000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SX_CLK_ECSPI1>,
@@ -263,7 +263,7 @@
ecspi2: ecspi@0200c000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sx-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sx-ecspi", "fsl,imx53-ecspi";
reg = <0x0200c000 0x4000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SX_CLK_ECSPI2>,
@@ -275,7 +275,7 @@
ecspi3: ecspi@02010000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sx-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sx-ecspi", "fsl,imx53-ecspi";
reg = <0x02010000 0x4000>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SX_CLK_ECSPI3>,
@@ -287,7 +287,7 @@
ecspi4: ecspi@02014000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6sx-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6sx-ecspi", "fsl,imx53-ecspi";
reg = <0x02014000 0x4000>;
interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SX_CLK_ECSPI4>,
diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi
index b9d7d2d..d35ad5b 100644
--- a/arch/arm/boot/dts/imx6ul.dtsi
+++ b/arch/arm/boot/dts/imx6ul.dtsi
@@ -204,7 +204,7 @@
ecspi1: ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6ul-ecspi", "fsl,imx53-ecspi";
reg = <0x02008000 0x4000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ECSPI1>,
@@ -216,7 +216,7 @@
ecspi2: ecspi@0200c000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6ul-ecspi", "fsl,imx53-ecspi";
reg = <0x0200c000 0x4000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ECSPI2>,
@@ -228,7 +228,7 @@
ecspi3: ecspi@02010000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6ul-ecspi", "fsl,imx53-ecspi";
reg = <0x02010000 0x4000>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ECSPI3>,
@@ -240,7 +240,7 @@
ecspi4: ecspi@02014000 {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
+ compatible = "fsl,imx6ul-ecspi", "fsl,imx53-ecspi";
reg = <0x02014000 0x4000>;
interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ECSPI4>,
--
2.7.4
^ permalink raw reply related
* [PATCH RFC 3/5] spi: imx: add selection for iMX53 and iMX6 controller
From: jiada_wang @ 2017-04-13 12:14 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland, shawnguo, kernel, fabio.estevam
Cc: linux-spi, devicetree, linux-kernel, linux-arm-kernel, Jiada Wang
In-Reply-To: <1492085644-4195-1-git-send-email-jiada_wang@mentor.com>
From: Jiada Wang <jiada_wang@mentor.com>
ECSPI contorller for iMX53 and iMX6 has few hardware issues
comparing to iMX51.
The change add possibility to detect which controller is used
to apply possible workaround and limitations.
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/spi/spi-imx.c | 43 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 9a7c62f..b2323b9 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -67,7 +67,8 @@ enum spi_imx_devtype {
IMX27_CSPI,
IMX31_CSPI,
IMX35_CSPI, /* CSPI on all i.mx except above */
- IMX51_ECSPI, /* ECSPI on i.mx51 and later */
+ IMX51_ECSPI, /* ECSPI on i.mx51 */
+ IMX53_ECSPI, /* ECSPI on i.mx53 and later */
};
struct spi_imx_data;
@@ -127,9 +128,32 @@ static inline int is_imx51_ecspi(struct spi_imx_data *d)
return d->devtype_data->devtype == IMX51_ECSPI;
}
+static inline int is_imx53_ecspi(struct spi_imx_data *d)
+{
+ return d->devtype_data->devtype == IMX53_ECSPI;
+}
+
static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d)
{
- return is_imx51_ecspi(d) ? 64 : 8;
+ switch (d->devtype_data->devtype) {
+ case IMX51_ECSPI:
+ case IMX53_ECSPI:
+ return 64;
+ default:
+ return 8;
+ }
+}
+
+static inline bool spi_imx_has_dmamode(struct spi_imx_data *d)
+{
+ switch (d->devtype_data->devtype) {
+ case IMX35_CSPI:
+ case IMX51_ECSPI:
+ case IMX53_ECSPI:
+ return true;
+ default:
+ return false;
+ }
}
#define MXC_SPI_BUF_RX(type) \
@@ -746,6 +770,15 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
.devtype = IMX51_ECSPI,
};
+static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
+ .intctrl = mx51_ecspi_intctrl,
+ .config = mx51_ecspi_config,
+ .trigger = mx51_ecspi_trigger,
+ .rx_available = mx51_ecspi_rx_available,
+ .reset = mx51_ecspi_reset,
+ .devtype = IMX53_ECSPI,
+};
+
static const struct platform_device_id spi_imx_devtype[] = {
{
.name = "imx1-cspi",
@@ -766,6 +799,9 @@ static const struct platform_device_id spi_imx_devtype[] = {
.name = "imx51-ecspi",
.driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data,
}, {
+ .name = "imx53-ecspi",
+ .driver_data = (kernel_ulong_t) &imx53_ecspi_devtype_data,
+ }, {
/* sentinel */
}
};
@@ -777,6 +813,7 @@ static const struct of_device_id spi_imx_dt_ids[] = {
{ .compatible = "fsl,imx31-cspi", .data = &imx31_cspi_devtype_data, },
{ .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, },
{ .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
+ { .compatible = "fsl,imx53-ecspi", .data = &imx53_ecspi_devtype_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
@@ -1266,7 +1303,7 @@ static int spi_imx_probe(struct platform_device *pdev)
* Only validated on i.mx35 and i.mx6 now, can remove the constraint
* if validated on other chips.
*/
- if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx)) {
+ if (spi_imx_has_dmamode(spi_imx)) {
ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master);
if (ret == -EPROBE_DEFER)
goto out_clk_put;
--
2.7.4
^ permalink raw reply related
* [PATCH RFC 2/5] spi: spidev: use different name for SPI controller slave mode device
From: jiada_wang @ 2017-04-13 12:14 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland, shawnguo, kernel, fabio.estevam
Cc: Jiada Wang, devicetree, linux-kernel, linux-arm-kernel, linux-spi
In-Reply-To: <1492085644-4195-1-git-send-email-jiada_wang@mentor.com>
From: Jiada Wang <jiada_wang@mentor.com>
SPI bus controller has started to support to work in slave mode,
for device SPI controller itself works in slave mode, use name
'spidev[bus]-slv' as its name to differentiate from other
SPI devices
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
drivers/spi/spidev.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 9e2e099..e2996fb 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -781,9 +781,18 @@ static int spidev_probe(struct spi_device *spi)
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
- dev = device_create(spidev_class, &spi->dev, spidev->devt,
- spidev, "spidev%d.%d",
- spi->master->bus_num, spi->chip_select);
+ if (spi->slave_mode)
+ dev = device_create(spidev_class, &spi->dev,
+ spidev->devt, spidev,
+ "spidev%d-slv",
+ spi->master->bus_num);
+ else
+ dev = device_create(spidev_class, &spi->dev,
+ spidev->devt, spidev,
+ "spidev%d.%d",
+ spi->master->bus_num,
+ spi->chip_select);
+
status = PTR_ERR_OR_ZERO(dev);
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
--
2.7.4
^ permalink raw reply related
* [PATCH RFC 1/5] spi: core: add support to work in Slave mode
From: jiada_wang-nmGgyN9QBj3QT0dZR+AlfA @ 2017-04-13 12:14 UTC (permalink / raw)
To: broonie-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, fabio.estevam-3arQi8VN3Tc
Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jiada Wang
In-Reply-To: <1492085644-4195-1-git-send-email-jiada_wang-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
From: Jiada Wang <jiada_wang-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
Add support for SPI bus controller to work in slave mode using
the existing SPI master framework.
- SPI device on SPI bus controller with 'spi-slave' property
declared in DT node represents SPI controller itself to work
as a slave device and listening to external SPI master devices
- when SPI bus controller works in slave mode, 'chip_select' and
'max_speed_hz' are not required.
- SPI slave mode continue to use 'struct spi_master'
Signed-off-by: Jiada Wang <jiada_wang-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
---
Documentation/devicetree/bindings/spi/spi-bus.txt | 27 ++++++++++++++---------
Documentation/spi/spi-summary | 19 +++++++++++-----
drivers/spi/Kconfig | 14 +++++++++++-
drivers/spi/spi.c | 23 ++++++++++++++++++-
include/linux/spi/spi.h | 15 +++++++++++++
5 files changed, 80 insertions(+), 18 deletions(-)
diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt
index 4b1d6e7..96e93ba 100644
--- a/Documentation/devicetree/bindings/spi/spi-bus.txt
+++ b/Documentation/devicetree/bindings/spi/spi-bus.txt
@@ -1,17 +1,20 @@
SPI (Serial Peripheral Interface) busses
-SPI busses can be described with a node for the SPI master device
-and a set of child nodes for each SPI slave on the bus. For this
-discussion, it is assumed that the system's SPI controller is in
-SPI master mode. This binding does not describe SPI controllers
-in slave mode.
+SPI busses can be described with a node for the SPI controller device
+and a set of child nodes for each SPI slave on the bus. The system's SPI
+controller can work either in master mode or in slave mode, based on the
+child node on it.
-The SPI master node requires the following properties:
+The SPI controller node requires the following properties:
+- compatible - name of SPI bus controller following generic names
+ recommended practice.
+
+In master mode, the SPI controller node requires the following additional
+properties:
- #address-cells - number of cells required to define a chip select
address on the SPI bus.
- #size-cells - should be zero.
-- compatible - name of SPI bus controller following generic names
- recommended practice.
+
No other properties are required in the SPI bus node. It is assumed
that a driver for an SPI bus device will understand that it is an SPI bus.
However, the binding does not attempt to define the specific method for
@@ -43,10 +46,11 @@ cs3 : &gpio1 2 0
SPI slave nodes must be children of the SPI master node and can
contain the following properties.
-- reg - (required) chip select address of device.
+- reg - (required, master mode only) chip select address of device.
- compatible - (required) name of SPI device following generic names
recommended practice.
-- spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz.
+- spi-max-frequency - (required, master mode only) Maximum SPI clocking speed of
+ device in Hz.
- spi-cpol - (optional) Empty property indicating device requires
inverse clock polarity (CPOL) mode.
- spi-cpha - (optional) Empty property indicating device requires
@@ -63,6 +67,9 @@ contain the following properties.
used for MISO. Defaults to 1 if not present.
- spi-rx-delay-us - (optional) Microsecond delay after a read transfer.
- spi-tx-delay-us - (optional) Microsecond delay after a write transfer.
+- spi-slave - (optional) Empty property indicating SPI bus controller
+ itself works in slave mode to interface with external master
+ devices.
Some SPI controllers and devices support Dual and Quad SPI transfer mode.
It allows data in the SPI system to be transferred using 2 wires (DUAL) or 4
diff --git a/Documentation/spi/spi-summary b/Documentation/spi/spi-summary
index d1824b3..4c2ceaa 100644
--- a/Documentation/spi/spi-summary
+++ b/Documentation/spi/spi-summary
@@ -62,9 +62,8 @@ chips described as using "three wire" signaling: SCK, data, nCSx.
(That data line is sometimes called MOMI or SISO.)
Microcontrollers often support both master and slave sides of the SPI
-protocol. This document (and Linux) currently only supports the master
-side of SPI interactions.
-
+protocol. This document (and Linux) supports both the master and slave
+sides of SPI interactions.
Who uses it? On what kinds of systems?
---------------------------------------
@@ -154,9 +153,8 @@ control audio interfaces, present touchscreen sensors as input interfaces,
or monitor temperature and voltage levels during industrial processing.
And those might all be sharing the same controller driver.
-A "struct spi_device" encapsulates the master-side interface between
-those two types of driver. At this writing, Linux has no slave side
-programming interface.
+A "struct spi_device" encapsulates the controller-side interface between
+those two types of drivers.
There is a minimal core of SPI programming interfaces, focussing on
using the driver model to connect controller and protocol drivers using
@@ -168,12 +166,21 @@ shows up in sysfs in several locations:
/sys/devices/.../CTLR/spiB.C ... spi_device on bus "B",
chipselect C, accessed through CTLR.
+ /sys/devices/.../CTLR/spiB-slv ... SPI bus "B" controller itself as a
+ spi_device works in slave mode, accessed through CTRL.
+
/sys/bus/spi/devices/spiB.C ... symlink to that physical
.../CTLR/spiB.C device
+ /sys/bus/spi/devices/spiB-slv ... symlink to that physical
+ .../CTLR/spiB-slv device
+
/sys/devices/.../CTLR/spiB.C/modalias ... identifies the driver
that should be used with this device (for hotplug/coldplug)
+ /sys/devices/.../CTLR/spiB-slv/modalias ... identifies the driver
+ that should be used with this device (for hotplug/coldplug)
+
/sys/bus/spi/drivers/D ... driver for one or more spi*.* devices
/sys/class/spi_master/spiB ... symlink (or actual device node) to
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 25ae7f2e..1096c7d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -784,6 +784,18 @@ config SPI_TLE62X0
endif # SPI_MASTER
-# (slave support would go here)
+#
+# SLAVE side ... listening to other SPI masters
+#
+
+config SPI_SLAVE
+ bool "SPI slave protocol handlers"
+ help
+ If your system has a slave-capable SPI controller, you can enable
+ slave protocol handlers.
+
+if SPI_SLAVE
+
+endif # SPI_SLAVE
endif # SPI
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 90b5b2e..3af26e2 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -475,6 +475,12 @@ static void spi_dev_set_name(struct spi_device *spi)
return;
}
+ if (spi->slave_mode) {
+ dev_set_name(&spi->dev, "%s-slv",
+ dev_name(&spi->master->dev));
+ return;
+ }
+
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
}
@@ -484,6 +490,9 @@ static int spi_dev_check(struct device *dev, void *data)
struct spi_device *spi = to_spi_device(dev);
struct spi_device *new_spi = data;
+ if (spi->slave_mode)
+ return 0;
+
if (spi->master == new_spi->master &&
spi->chip_select == new_spi->chip_select)
return -EBUSY;
@@ -523,6 +532,9 @@ int spi_add_device(struct spi_device *spi)
*/
mutex_lock(&spi_add_lock);
+ if (spi->slave_mode)
+ goto setup_spi;
+
status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
if (status) {
dev_err(dev, "chipselect %d already in use\n",
@@ -533,6 +545,7 @@ int spi_add_device(struct spi_device *spi)
if (master->cs_gpios)
spi->cs_gpio = master->cs_gpios[spi->chip_select];
+setup_spi:
/* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
@@ -1511,6 +1524,14 @@ static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi,
u32 value;
int rc;
+ if (of_find_property(nc, "spi-slave", NULL)) {
+ if (!spi_controller_has_slavemode(master))
+ return -EINVAL;
+
+ spi->slave_mode = 1;
+ return 0;
+ }
+
/* Device address */
rc = of_property_read_u32(nc, "reg", &value);
if (rc) {
@@ -1961,7 +1982,7 @@ int spi_register_master(struct spi_master *master)
status = device_add(&master->dev);
if (status < 0)
goto done;
- dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
+ dev_dbg(dev, "registered controller %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
/* If we're using a queued driver, start the queue */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 75c6bd0..bb81425 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -115,6 +115,8 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
* This may be changed by the device's driver, or left at the
* default (0) indicating protocol words are eight bit bytes.
* The spi_transfer.bits_per_word can override this for each transfer.
+ * @slave_mode: indicates whether SPI controller works in master mode
+ * or slave mode to transfer data with external spi devices.
* @irq: Negative, or the number passed to request_irq() to receive
* interrupts from this device.
* @controller_state: Controller's runtime state
@@ -144,6 +146,7 @@ struct spi_device {
u8 chip_select;
u8 bits_per_word;
u16 mode;
+ u8 slave_mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
@@ -372,6 +375,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* transfer_one callback.
* @handle_err: the subsystem calls the driver to handle an error that occurs
* in the generic implementation of transfer_one_message().
+ * @has_slavemode: checks whether SPI Controller supports slave mode or not.
* @unprepare_message: undo any work done by prepare_message().
* @spi_flash_read: to support spi-controller hardwares that provide
* accelerated interface to read from flash devices.
@@ -549,6 +553,7 @@ struct spi_master {
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
+ bool (*has_slavemode)(struct spi_master *master);
/* gpio chip select */
int *cs_gpios;
@@ -590,6 +595,16 @@ static inline void spi_master_put(struct spi_master *master)
put_device(&master->dev);
}
+
+static inline bool spi_controller_has_slavemode(struct spi_master *master)
+{
+#ifdef CONFIG_SPI_SLAVE
+ if (master->has_slavemode)
+ return master->has_slavemode(master);
+#endif
+ return false;
+}
+
/* PM calls that need to be issued by the driver */
extern int spi_master_suspend(struct spi_master *master);
extern int spi_master_resume(struct spi_master *master);
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH RFC 0/5] *** SPI Slave mode support ***
From: jiada_wang @ 2017-04-13 12:13 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland, shawnguo, kernel, fabio.estevam
Cc: Jiada Wang, devicetree, linux-kernel, linux-arm-kernel, linux-spi
From: Jiada Wang <jiada_wang@mentor.com>
v1:
add Slave mode support in SPI core
spidev create slave device when SPI controller work in slave mode
spi-imx support to work in slave mode
Jiada Wang (5):
spi: core: add support to work in Slave mode
spi: spidev: use different name for SPI controller slave mode device
spi: imx: add selection for iMX53 and iMX6 controller
ARM: dts: imx: change compatiblity for SPI controllers on imx53 later
soc
spi: imx: Add support for SPI Slave mode for imx53 and imx6 chips
Documentation/devicetree/bindings/spi/spi-bus.txt | 27 ++-
Documentation/spi/spi-summary | 19 +-
arch/arm/boot/dts/imx53.dtsi | 4 +-
arch/arm/boot/dts/imx6q.dtsi | 2 +-
arch/arm/boot/dts/imx6qdl.dtsi | 8 +-
arch/arm/boot/dts/imx6sl.dtsi | 8 +-
arch/arm/boot/dts/imx6sx.dtsi | 8 +-
arch/arm/boot/dts/imx6ul.dtsi | 8 +-
drivers/spi/Kconfig | 14 +-
drivers/spi/spi-imx.c | 216 ++++++++++++++++++++--
drivers/spi/spi.c | 23 ++-
drivers/spi/spidev.c | 15 +-
include/linux/spi/spi.h | 15 ++
13 files changed, 310 insertions(+), 57 deletions(-)
--
2.7.4
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox