Initial commit
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
From e43dec70614b55ba1ce24dfcdf8f51e36d800af2 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:26:46 +0800
|
||||
Subject: [PATCH 01/30] config: support layerscape
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This is an integrated patch for layerscape config/makefile support.
|
||||
|
||||
Signed-off-by: Yuantian Tang <andy.tang@nxp.com>
|
||||
Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
|
||||
Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
|
||||
Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
|
||||
Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
|
||||
Signed-off-by: Horia Geantă <horia.geanta@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
arch/arm/mach-imx/Kconfig | 1 +
|
||||
drivers/base/Kconfig | 1 +
|
||||
drivers/crypto/Makefile | 2 +-
|
||||
drivers/net/ethernet/freescale/Kconfig | 4 ++-
|
||||
drivers/net/ethernet/freescale/Makefile | 2 ++
|
||||
drivers/ptp/Kconfig | 29 +++++++++++++++++++
|
||||
drivers/rtc/Kconfig | 8 ++++++
|
||||
drivers/rtc/Makefile | 1 +
|
||||
drivers/soc/Kconfig | 3 +-
|
||||
drivers/soc/fsl/Kconfig | 22 ++++++++++++++
|
||||
drivers/soc/fsl/Kconfig.arm | 16 +++++++++++
|
||||
drivers/soc/fsl/Makefile | 4 +++
|
||||
drivers/soc/fsl/layerscape/Kconfig | 10 +++++++
|
||||
drivers/soc/fsl/layerscape/Makefile | 1 +
|
||||
drivers/staging/Kconfig | 6 ++++
|
||||
drivers/staging/Makefile | 3 ++
|
||||
drivers/staging/fsl-dpaa2/Kconfig | 51 +++++++++++++++++++++++++++++++++
|
||||
drivers/staging/fsl-dpaa2/Makefile | 9 ++++++
|
||||
18 files changed, 169 insertions(+), 4 deletions(-)
|
||||
create mode 100644 drivers/soc/fsl/Kconfig
|
||||
create mode 100644 drivers/soc/fsl/Kconfig.arm
|
||||
create mode 100644 drivers/soc/fsl/layerscape/Kconfig
|
||||
create mode 100644 drivers/soc/fsl/layerscape/Makefile
|
||||
create mode 100644 drivers/staging/fsl-dpaa2/Kconfig
|
||||
create mode 100644 drivers/staging/fsl-dpaa2/Makefile
|
||||
|
||||
--- a/arch/arm/mach-imx/Kconfig
|
||||
+++ b/arch/arm/mach-imx/Kconfig
|
||||
@@ -1,6 +1,7 @@
|
||||
menuconfig ARCH_MXC
|
||||
bool "Freescale i.MX family"
|
||||
depends on ARCH_MULTI_V4_V5 || ARCH_MULTI_V6_V7 || ARM_SINGLE_ARMV7M
|
||||
+ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
|
||||
select ARCH_SUPPORTS_BIG_ENDIAN
|
||||
select CLKSRC_IMX_GPT
|
||||
select GENERIC_IRQ_CHIP
|
||||
--- a/drivers/base/Kconfig
|
||||
+++ b/drivers/base/Kconfig
|
||||
@@ -240,6 +240,7 @@ config GENERIC_CPU_VULNERABILITIES
|
||||
|
||||
config SOC_BUS
|
||||
bool
|
||||
+ select GLOB
|
||||
|
||||
source "drivers/base/regmap/Kconfig"
|
||||
|
||||
--- a/drivers/crypto/Makefile
|
||||
+++ b/drivers/crypto/Makefile
|
||||
@@ -3,7 +3,7 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += at
|
||||
obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_BFIN_CRC) += bfin_crc.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/
|
||||
-obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/
|
||||
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_COMMON) += caam/
|
||||
obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
|
||||
--- a/drivers/net/ethernet/freescale/Kconfig
|
||||
+++ b/drivers/net/ethernet/freescale/Kconfig
|
||||
@@ -5,7 +5,7 @@
|
||||
config NET_VENDOR_FREESCALE
|
||||
bool "Freescale devices"
|
||||
default y
|
||||
- depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \
|
||||
+ depends on FSL_SOC || (QUICC_ENGINE && PPC32) || CPM1 || CPM2 || PPC_MPC512x || \
|
||||
M523x || M527x || M5272 || M528x || M520x || M532x || \
|
||||
ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) || \
|
||||
ARCH_LAYERSCAPE
|
||||
@@ -93,4 +93,6 @@ config GIANFAR
|
||||
and MPC86xx family of chips, the eTSEC on LS1021A and the FEC
|
||||
on the 8540.
|
||||
|
||||
+source "drivers/net/ethernet/freescale/sdk_fman/Kconfig"
|
||||
+source "drivers/net/ethernet/freescale/sdk_dpaa/Kconfig"
|
||||
endif # NET_VENDOR_FREESCALE
|
||||
--- a/drivers/net/ethernet/freescale/Makefile
|
||||
+++ b/drivers/net/ethernet/freescale/Makefile
|
||||
@@ -21,4 +21,6 @@ gianfar_driver-objs := gianfar.o \
|
||||
obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
|
||||
ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
|
||||
|
||||
+obj-$(if $(CONFIG_FSL_SDK_FMAN),y) += sdk_fman/
|
||||
+obj-$(if $(CONFIG_FSL_SDK_DPAA_ETH),y) += sdk_dpaa/
|
||||
obj-$(CONFIG_FSL_FMAN) += fman/
|
||||
--- a/drivers/ptp/Kconfig
|
||||
+++ b/drivers/ptp/Kconfig
|
||||
@@ -39,6 +39,35 @@ config PTP_1588_CLOCK_GIANFAR
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called gianfar_ptp.
|
||||
|
||||
+config PTP_1588_CLOCK_DPAA
|
||||
+ tristate "Freescale DPAA as PTP clock"
|
||||
+ depends on FSL_SDK_DPAA_ETH
|
||||
+ select PTP_1588_CLOCK
|
||||
+ select FSL_DPAA_TS
|
||||
+ default n
|
||||
+ help
|
||||
+ This driver adds support for using the DPAA 1588 timer module
|
||||
+ as a PTP clock. This clock is only useful if your PTP programs are
|
||||
+ getting hardware time stamps on the PTP Ethernet packets
|
||||
+ using the SO_TIMESTAMPING API.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the module
|
||||
+ will be called dpaa_ptp.
|
||||
+
|
||||
+config PTP_1588_CLOCK_DPAA2
|
||||
+ tristate "Freescale DPAA2 as PTP clock"
|
||||
+ depends on FSL_DPAA2_ETH
|
||||
+ select PTP_1588_CLOCK
|
||||
+ default y
|
||||
+ help
|
||||
+ This driver adds support for using the DPAA2 1588 timer module
|
||||
+ as a PTP clock. This clock is only useful if your PTP programs are
|
||||
+ getting hardware time stamps on the PTP Ethernet packets
|
||||
+ using the SO_TIMESTAMPING API.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the module
|
||||
+ will be called dpaa2-rtc.
|
||||
+
|
||||
config PTP_1588_CLOCK_IXP46X
|
||||
tristate "Intel IXP46x as PTP clock"
|
||||
depends on IXP4XX_ETH
|
||||
--- a/drivers/rtc/Kconfig
|
||||
+++ b/drivers/rtc/Kconfig
|
||||
@@ -415,6 +415,14 @@ config RTC_DRV_PCF85063
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf85063.
|
||||
|
||||
+config RTC_DRV_PCF85263
|
||||
+ tristate "NXP PCF85263"
|
||||
+ help
|
||||
+ If you say yes here you get support for the PCF85263 RTC chip
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module
|
||||
+ will be called rtc-pcf85263.
|
||||
+
|
||||
config RTC_DRV_PCF8563
|
||||
tristate "Philips PCF8563/Epson RTC8564"
|
||||
help
|
||||
--- a/drivers/rtc/Makefile
|
||||
+++ b/drivers/rtc/Makefile
|
||||
@@ -111,6 +111,7 @@ obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf
|
||||
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
|
||||
+obj-$(CONFIG_RTC_DRV_PCF85263) += rtc-pcf85263.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
|
||||
obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o
|
||||
--- a/drivers/soc/Kconfig
|
||||
+++ b/drivers/soc/Kconfig
|
||||
@@ -1,8 +1,7 @@
|
||||
menu "SOC (System On Chip) specific Drivers"
|
||||
|
||||
source "drivers/soc/bcm/Kconfig"
|
||||
-source "drivers/soc/fsl/qbman/Kconfig"
|
||||
-source "drivers/soc/fsl/qe/Kconfig"
|
||||
+source "drivers/soc/fsl/Kconfig"
|
||||
source "drivers/soc/mediatek/Kconfig"
|
||||
source "drivers/soc/qcom/Kconfig"
|
||||
source "drivers/soc/rockchip/Kconfig"
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/Kconfig
|
||||
@@ -0,0 +1,22 @@
|
||||
+#
|
||||
+# Freescale SOC drivers
|
||||
+#
|
||||
+
|
||||
+source "drivers/soc/fsl/qbman/Kconfig"
|
||||
+source "drivers/soc/fsl/qe/Kconfig"
|
||||
+source "drivers/soc/fsl/ls2-console/Kconfig"
|
||||
+
|
||||
+config FSL_GUTS
|
||||
+ bool
|
||||
+ select SOC_BUS
|
||||
+ help
|
||||
+ The global utilities block controls power management, I/O device
|
||||
+ enabling, power-onreset(POR) configuration monitoring, alternate
|
||||
+ function selection for multiplexed signals,and clock control.
|
||||
+ This driver is to manage and access global utilities block.
|
||||
+ Initially only reading SVR and registering soc device are supported.
|
||||
+ Other guts accesses, such as reading RCW, should eventually be moved
|
||||
+ into this driver as well.
|
||||
+if ARM || ARM64
|
||||
+source "drivers/soc/fsl/Kconfig.arm"
|
||||
+endif
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/Kconfig.arm
|
||||
@@ -0,0 +1,16 @@
|
||||
+#
|
||||
+# Freescale ARM SOC Drivers
|
||||
+#
|
||||
+
|
||||
+config LS_SOC_DRIVERS
|
||||
+ bool "Layerscape Soc Drivers"
|
||||
+ depends on ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
+ default n
|
||||
+ help
|
||||
+ Say y here to enable Freescale Layerscape Soc Device Drivers support.
|
||||
+ The Soc Drivers provides the device driver that is a specific block
|
||||
+ or feature on Layerscape platform.
|
||||
+
|
||||
+if LS_SOC_DRIVERS
|
||||
+ source "drivers/soc/fsl/layerscape/Kconfig"
|
||||
+endif
|
||||
--- a/drivers/soc/fsl/Makefile
|
||||
+++ b/drivers/soc/fsl/Makefile
|
||||
@@ -5,3 +5,7 @@
|
||||
obj-$(CONFIG_FSL_DPAA) += qbman/
|
||||
obj-$(CONFIG_QUICC_ENGINE) += qe/
|
||||
obj-$(CONFIG_CPM) += qe/
|
||||
+obj-$(CONFIG_FSL_GUTS) += guts.o
|
||||
+obj-$(CONFIG_FSL_LS2_CONSOLE) += ls2-console/
|
||||
+obj-$(CONFIG_SUSPEND) += rcpm.o
|
||||
+obj-$(CONFIG_LS_SOC_DRIVERS) += layerscape/
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/layerscape/Kconfig
|
||||
@@ -0,0 +1,10 @@
|
||||
+#
|
||||
+# Layerscape Soc drivers
|
||||
+#
|
||||
+config FTM_ALARM
|
||||
+ bool "FTM alarm driver"
|
||||
+ default n
|
||||
+ help
|
||||
+ Say y here to enable FTM alarm support. The FTM alarm provides
|
||||
+ alarm functions for wakeup system from deep sleep. There is only
|
||||
+ one FTM can be used in ALARM(FTM 0).
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/layerscape/Makefile
|
||||
@@ -0,0 +1 @@
|
||||
+obj-$(CONFIG_FTM_ALARM) += ftm_alarm.o
|
||||
--- a/drivers/staging/Kconfig
|
||||
+++ b/drivers/staging/Kconfig
|
||||
@@ -94,6 +94,8 @@ source "drivers/staging/fbtft/Kconfig"
|
||||
|
||||
source "drivers/staging/fsl-mc/Kconfig"
|
||||
|
||||
+source "drivers/staging/fsl-dpaa2/Kconfig"
|
||||
+
|
||||
source "drivers/staging/wilc1000/Kconfig"
|
||||
|
||||
source "drivers/staging/most/Kconfig"
|
||||
@@ -106,4 +108,8 @@ source "drivers/staging/greybus/Kconfig"
|
||||
|
||||
source "drivers/staging/vc04_services/Kconfig"
|
||||
|
||||
+source "drivers/staging/fsl_qbman/Kconfig"
|
||||
+
|
||||
+source "drivers/staging/fsl_ppfe/Kconfig"
|
||||
+
|
||||
endif # STAGING
|
||||
--- a/drivers/staging/Makefile
|
||||
+++ b/drivers/staging/Makefile
|
||||
@@ -36,9 +36,12 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
|
||||
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
|
||||
obj-$(CONFIG_FB_TFT) += fbtft/
|
||||
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
||||
+obj-$(CONFIG_FSL_DPAA2) += fsl-dpaa2/
|
||||
obj-$(CONFIG_WILC1000) += wilc1000/
|
||||
obj-$(CONFIG_MOST) += most/
|
||||
obj-$(CONFIG_ISDN_I4L) += i4l/
|
||||
obj-$(CONFIG_KS7010) += ks7010/
|
||||
obj-$(CONFIG_GREYBUS) += greybus/
|
||||
obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/
|
||||
+obj-$(CONFIG_FSL_SDK_DPA) += fsl_qbman/
|
||||
+obj-$(CONFIG_FSL_PPFE) += fsl_ppfe/
|
||||
--- /dev/null
|
||||
+++ b/drivers/staging/fsl-dpaa2/Kconfig
|
||||
@@ -0,0 +1,51 @@
|
||||
+#
|
||||
+# Freescale DataPath Acceleration Architecture Gen2 (DPAA2) drivers
|
||||
+#
|
||||
+
|
||||
+config FSL_DPAA2
|
||||
+ bool "Freescale DPAA2 devices"
|
||||
+ depends on FSL_MC_BUS
|
||||
+ ---help---
|
||||
+ Build drivers for Freescale DataPath Acceleration
|
||||
+ Architecture (DPAA2) family of SoCs.
|
||||
+
|
||||
+config FSL_DPAA2_ETH
|
||||
+ tristate "Freescale DPAA2 Ethernet"
|
||||
+ depends on FSL_DPAA2 && FSL_MC_DPIO
|
||||
+ ---help---
|
||||
+ Ethernet driver for Freescale DPAA2 SoCs, using the
|
||||
+ Freescale MC bus driver
|
||||
+
|
||||
+if FSL_DPAA2_ETH
|
||||
+config FSL_DPAA2_ETH_USE_ERR_QUEUE
|
||||
+ bool "Enable Rx error queue"
|
||||
+ default n
|
||||
+ ---help---
|
||||
+ Allow Rx error frames to be enqueued on an error queue
|
||||
+ and processed by the driver (by default they are dropped
|
||||
+ in hardware).
|
||||
+ This may impact performance, recommended for debugging
|
||||
+ purposes only.
|
||||
+
|
||||
+# QBMAN_DEBUG requires some additional DPIO APIs
|
||||
+config FSL_DPAA2_ETH_DEBUGFS
|
||||
+ depends on DEBUG_FS && FSL_QBMAN_DEBUG
|
||||
+ bool "Enable debugfs support"
|
||||
+ default n
|
||||
+ ---help---
|
||||
+ Enable advanced statistics through debugfs interface.
|
||||
+
|
||||
+config FSL_DPAA2_ETH_DCB
|
||||
+ bool "Data Center Bridging (DCB) Support"
|
||||
+ default n
|
||||
+ depends on DCB
|
||||
+ ---help---
|
||||
+ Say Y here if you want to use Data Center Bridging (DCB) features
|
||||
+ (PFC) in the driver.
|
||||
+
|
||||
+ If unsure, say N.
|
||||
+endif
|
||||
+
|
||||
+source "drivers/staging/fsl-dpaa2/mac/Kconfig"
|
||||
+source "drivers/staging/fsl-dpaa2/evb/Kconfig"
|
||||
+source "drivers/staging/fsl-dpaa2/ethsw/Kconfig"
|
||||
--- /dev/null
|
||||
+++ b/drivers/staging/fsl-dpaa2/Makefile
|
||||
@@ -0,0 +1,9 @@
|
||||
+#
|
||||
+# Freescale DataPath Acceleration Architecture Gen2 (DPAA2) drivers
|
||||
+#
|
||||
+
|
||||
+obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/
|
||||
+obj-$(CONFIG_FSL_DPAA2_MAC) += mac/
|
||||
+obj-$(CONFIG_FSL_DPAA2_EVB) += evb/
|
||||
+obj-$(CONFIG_FSL_DPAA2_ETHSW) += ethsw/
|
||||
+obj-$(CONFIG_PTP_1588_CLOCK_DPAA2) += rtc/
|
||||
@@ -0,0 +1,512 @@
|
||||
From 67a2eceebe9dcd92a1a5f3e912340c8975c84434 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 14:50:41 +0800
|
||||
Subject: [PATCH 02/30] core-linux: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape core-linux support.
|
||||
|
||||
Signed-off-by: Madalin Bucur <madalin.bucur@freescale.com>
|
||||
Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
|
||||
Signed-off-by: Camelia Groza <camelia.groza@nxp.com>
|
||||
Signed-off-by: Madalin Bucur <madalin.bucur@nxp.com>
|
||||
Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
|
||||
Signed-off-by: Ramneek Mehresh <ramneek.mehresh@freescale.com>
|
||||
Signed-off-by: Jarod Wilson <jarod@redhat.com>
|
||||
Signed-off-by: Nikhil Badola <nikhil.badola@freescale.com>
|
||||
Signed-off-by: stephen hemminger <stephen@networkplumber.org>
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/base/devres.c | 66 ++++++++++++++++++++++++++++
|
||||
drivers/base/soc.c | 70 +++++++++++++++++++++++++++++
|
||||
include/linux/device.h | 19 ++++++++
|
||||
include/linux/fsl/svr.h | 97 +++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/fsl_devices.h | 3 ++
|
||||
include/linux/netdev_features.h | 2 +
|
||||
include/linux/netdevice.h | 4 ++
|
||||
include/linux/skbuff.h | 2 +
|
||||
include/linux/sys_soc.h | 3 ++
|
||||
include/uapi/linux/if_ether.h | 1 +
|
||||
net/core/dev.c | 13 +++++-
|
||||
net/core/skbuff.c | 29 +++++++++++-
|
||||
net/sched/sch_generic.c | 7 +++
|
||||
13 files changed, 313 insertions(+), 3 deletions(-)
|
||||
create mode 100644 include/linux/fsl/svr.h
|
||||
|
||||
--- a/drivers/base/devres.c
|
||||
+++ b/drivers/base/devres.c
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
+#include <linux/percpu.h>
|
||||
|
||||
#include "base.h"
|
||||
|
||||
@@ -985,3 +986,68 @@ void devm_free_pages(struct device *dev,
|
||||
&devres));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_free_pages);
|
||||
+
|
||||
+static void devm_percpu_release(struct device *dev, void *pdata)
|
||||
+{
|
||||
+ void __percpu *p;
|
||||
+
|
||||
+ p = *(void __percpu **)pdata;
|
||||
+ free_percpu(p);
|
||||
+}
|
||||
+
|
||||
+static int devm_percpu_match(struct device *dev, void *data, void *p)
|
||||
+{
|
||||
+ struct devres *devr = container_of(data, struct devres, data);
|
||||
+
|
||||
+ return *(void **)devr->data == p;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __devm_alloc_percpu - Resource-managed alloc_percpu
|
||||
+ * @dev: Device to allocate per-cpu memory for
|
||||
+ * @size: Size of per-cpu memory to allocate
|
||||
+ * @align: Alignment of per-cpu memory to allocate
|
||||
+ *
|
||||
+ * Managed alloc_percpu. Per-cpu memory allocated with this function is
|
||||
+ * automatically freed on driver detach.
|
||||
+ *
|
||||
+ * RETURNS:
|
||||
+ * Pointer to allocated memory on success, NULL on failure.
|
||||
+ */
|
||||
+void __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
|
||||
+ size_t align)
|
||||
+{
|
||||
+ void *p;
|
||||
+ void __percpu *pcpu;
|
||||
+
|
||||
+ pcpu = __alloc_percpu(size, align);
|
||||
+ if (!pcpu)
|
||||
+ return NULL;
|
||||
+
|
||||
+ p = devres_alloc(devm_percpu_release, sizeof(void *), GFP_KERNEL);
|
||||
+ if (!p) {
|
||||
+ free_percpu(pcpu);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ *(void __percpu **)p = pcpu;
|
||||
+
|
||||
+ devres_add(dev, p);
|
||||
+
|
||||
+ return pcpu;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(__devm_alloc_percpu);
|
||||
+
|
||||
+/**
|
||||
+ * devm_free_percpu - Resource-managed free_percpu
|
||||
+ * @dev: Device this memory belongs to
|
||||
+ * @pdata: Per-cpu memory to free
|
||||
+ *
|
||||
+ * Free memory allocated with devm_alloc_percpu().
|
||||
+ */
|
||||
+void devm_free_percpu(struct device *dev, void __percpu *pdata)
|
||||
+{
|
||||
+ WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match,
|
||||
+ (void *)pdata));
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(devm_free_percpu);
|
||||
--- a/drivers/base/soc.c
|
||||
+++ b/drivers/base/soc.c
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/err.h>
|
||||
+#include <linux/glob.h>
|
||||
|
||||
static DEFINE_IDA(soc_ida);
|
||||
|
||||
@@ -161,3 +162,72 @@ static int __init soc_bus_register(void)
|
||||
return bus_register(&soc_bus_type);
|
||||
}
|
||||
core_initcall(soc_bus_register);
|
||||
+
|
||||
+static int soc_device_match_one(struct device *dev, void *arg)
|
||||
+{
|
||||
+ struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
|
||||
+ const struct soc_device_attribute *match = arg;
|
||||
+
|
||||
+ if (match->machine &&
|
||||
+ (!soc_dev->attr->machine ||
|
||||
+ !glob_match(match->machine, soc_dev->attr->machine)))
|
||||
+ return 0;
|
||||
+
|
||||
+ if (match->family &&
|
||||
+ (!soc_dev->attr->family ||
|
||||
+ !glob_match(match->family, soc_dev->attr->family)))
|
||||
+ return 0;
|
||||
+
|
||||
+ if (match->revision &&
|
||||
+ (!soc_dev->attr->revision ||
|
||||
+ !glob_match(match->revision, soc_dev->attr->revision)))
|
||||
+ return 0;
|
||||
+
|
||||
+ if (match->soc_id &&
|
||||
+ (!soc_dev->attr->soc_id ||
|
||||
+ !glob_match(match->soc_id, soc_dev->attr->soc_id)))
|
||||
+ return 0;
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * soc_device_match - identify the SoC in the machine
|
||||
+ * @matches: zero-terminated array of possible matches
|
||||
+ *
|
||||
+ * returns the first matching entry of the argument array, or NULL
|
||||
+ * if none of them match.
|
||||
+ *
|
||||
+ * This function is meant as a helper in place of of_match_node()
|
||||
+ * in cases where either no device tree is available or the information
|
||||
+ * in a device node is insufficient to identify a particular variant
|
||||
+ * by its compatible strings or other properties. For new devices,
|
||||
+ * the DT binding should always provide unique compatible strings
|
||||
+ * that allow the use of of_match_node() instead.
|
||||
+ *
|
||||
+ * The calling function can use the .data entry of the
|
||||
+ * soc_device_attribute to pass a structure or function pointer for
|
||||
+ * each entry.
|
||||
+ */
|
||||
+const struct soc_device_attribute *soc_device_match(
|
||||
+ const struct soc_device_attribute *matches)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (!matches)
|
||||
+ return NULL;
|
||||
+
|
||||
+ while (!ret) {
|
||||
+ if (!(matches->machine || matches->family ||
|
||||
+ matches->revision || matches->soc_id))
|
||||
+ break;
|
||||
+ ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
|
||||
+ soc_device_match_one);
|
||||
+ if (!ret)
|
||||
+ matches++;
|
||||
+ else
|
||||
+ return matches;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(soc_device_match);
|
||||
--- a/include/linux/device.h
|
||||
+++ b/include/linux/device.h
|
||||
@@ -689,6 +689,25 @@ void __iomem *devm_ioremap_resource(stru
|
||||
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
|
||||
void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
|
||||
|
||||
+/**
|
||||
+ * devm_alloc_percpu - Resource-managed alloc_percpu
|
||||
+ * @dev: Device to allocate per-cpu memory for
|
||||
+ * @type: Type to allocate per-cpu memory for
|
||||
+ *
|
||||
+ * Managed alloc_percpu. Per-cpu memory allocated with this function is
|
||||
+ * automatically freed on driver detach.
|
||||
+ *
|
||||
+ * RETURNS:
|
||||
+ * Pointer to allocated memory on success, NULL on failure.
|
||||
+ */
|
||||
+#define devm_alloc_percpu(dev, type) \
|
||||
+ ((typeof(type) __percpu *)__devm_alloc_percpu((dev), sizeof(type), \
|
||||
+ __alignof__(type)))
|
||||
+
|
||||
+void __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
|
||||
+ size_t align);
|
||||
+void devm_free_percpu(struct device *dev, void __percpu *pdata);
|
||||
+
|
||||
static inline int devm_add_action_or_reset(struct device *dev,
|
||||
void (*action)(void *), void *data)
|
||||
{
|
||||
--- /dev/null
|
||||
+++ b/include/linux/fsl/svr.h
|
||||
@@ -0,0 +1,97 @@
|
||||
+/*
|
||||
+ * MPC85xx cpu type detection
|
||||
+ *
|
||||
+ * Copyright 2011-2012 Freescale Semiconductor, Inc.
|
||||
+ *
|
||||
+ * This is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ */
|
||||
+
|
||||
+#ifndef FSL_SVR_H
|
||||
+#define FSL_SVR_H
|
||||
+
|
||||
+#define SVR_REV(svr) ((svr) & 0xFF) /* SOC design resision */
|
||||
+#define SVR_MAJ(svr) (((svr) >> 4) & 0xF) /* Major revision field*/
|
||||
+#define SVR_MIN(svr) (((svr) >> 0) & 0xF) /* Minor revision field*/
|
||||
+
|
||||
+/* Some parts define SVR[0:23] as the SOC version */
|
||||
+#define SVR_SOC_VER(svr) (((svr) >> 8) & 0xFFF7FF) /* SOC Version fields */
|
||||
+
|
||||
+#define SVR_8533 0x803400
|
||||
+#define SVR_8535 0x803701
|
||||
+#define SVR_8536 0x803700
|
||||
+#define SVR_8540 0x803000
|
||||
+#define SVR_8541 0x807200
|
||||
+#define SVR_8543 0x803200
|
||||
+#define SVR_8544 0x803401
|
||||
+#define SVR_8545 0x803102
|
||||
+#define SVR_8547 0x803101
|
||||
+#define SVR_8548 0x803100
|
||||
+#define SVR_8555 0x807100
|
||||
+#define SVR_8560 0x807000
|
||||
+#define SVR_8567 0x807501
|
||||
+#define SVR_8568 0x807500
|
||||
+#define SVR_8569 0x808000
|
||||
+#define SVR_8572 0x80E000
|
||||
+#define SVR_P1010 0x80F100
|
||||
+#define SVR_P1011 0x80E500
|
||||
+#define SVR_P1012 0x80E501
|
||||
+#define SVR_P1013 0x80E700
|
||||
+#define SVR_P1014 0x80F101
|
||||
+#define SVR_P1017 0x80F700
|
||||
+#define SVR_P1020 0x80E400
|
||||
+#define SVR_P1021 0x80E401
|
||||
+#define SVR_P1022 0x80E600
|
||||
+#define SVR_P1023 0x80F600
|
||||
+#define SVR_P1024 0x80E402
|
||||
+#define SVR_P1025 0x80E403
|
||||
+#define SVR_P2010 0x80E300
|
||||
+#define SVR_P2020 0x80E200
|
||||
+#define SVR_P2040 0x821000
|
||||
+#define SVR_P2041 0x821001
|
||||
+#define SVR_P3041 0x821103
|
||||
+#define SVR_P4040 0x820100
|
||||
+#define SVR_P4080 0x820000
|
||||
+#define SVR_P5010 0x822100
|
||||
+#define SVR_P5020 0x822000
|
||||
+#define SVR_P5021 0X820500
|
||||
+#define SVR_P5040 0x820400
|
||||
+#define SVR_T4240 0x824000
|
||||
+#define SVR_T4120 0x824001
|
||||
+#define SVR_T4160 0x824100
|
||||
+#define SVR_T4080 0x824102
|
||||
+#define SVR_C291 0x850000
|
||||
+#define SVR_C292 0x850020
|
||||
+#define SVR_C293 0x850030
|
||||
+#define SVR_B4860 0X868000
|
||||
+#define SVR_G4860 0x868001
|
||||
+#define SVR_G4060 0x868003
|
||||
+#define SVR_B4440 0x868100
|
||||
+#define SVR_G4440 0x868101
|
||||
+#define SVR_B4420 0x868102
|
||||
+#define SVR_B4220 0x868103
|
||||
+#define SVR_T1040 0x852000
|
||||
+#define SVR_T1041 0x852001
|
||||
+#define SVR_T1042 0x852002
|
||||
+#define SVR_T1020 0x852100
|
||||
+#define SVR_T1021 0x852101
|
||||
+#define SVR_T1022 0x852102
|
||||
+#define SVR_T1023 0x854100
|
||||
+#define SVR_T1024 0x854000
|
||||
+#define SVR_T2080 0x853000
|
||||
+#define SVR_T2081 0x853100
|
||||
+
|
||||
+#define SVR_8610 0x80A000
|
||||
+#define SVR_8641 0x809000
|
||||
+#define SVR_8641D 0x809001
|
||||
+
|
||||
+#define SVR_9130 0x860001
|
||||
+#define SVR_9131 0x860000
|
||||
+#define SVR_9132 0x861000
|
||||
+#define SVR_9232 0x861400
|
||||
+
|
||||
+#define SVR_Unknown 0xFFFFFF
|
||||
+
|
||||
+#endif
|
||||
--- a/include/linux/fsl_devices.h
|
||||
+++ b/include/linux/fsl_devices.h
|
||||
@@ -99,7 +99,10 @@ struct fsl_usb2_platform_data {
|
||||
unsigned suspended:1;
|
||||
unsigned already_suspended:1;
|
||||
unsigned has_fsl_erratum_a007792:1;
|
||||
+ unsigned has_fsl_erratum_14:1;
|
||||
unsigned has_fsl_erratum_a005275:1;
|
||||
+ unsigned has_fsl_erratum_a006918:1;
|
||||
+ unsigned has_fsl_erratum_a005697:1;
|
||||
unsigned check_phy_clk_valid:1;
|
||||
|
||||
/* register save area for suspend/resume */
|
||||
--- a/include/linux/netdev_features.h
|
||||
+++ b/include/linux/netdev_features.h
|
||||
@@ -76,6 +76,7 @@ enum {
|
||||
NETIF_F_BUSY_POLL_BIT, /* Busy poll */
|
||||
|
||||
NETIF_F_HW_TC_BIT, /* Offload TC infrastructure */
|
||||
+ NETIF_F_HW_ACCEL_MQ_BIT, /* Hardware-accelerated multiqueue */
|
||||
|
||||
/*
|
||||
* Add your fresh new feature above and remember to update
|
||||
@@ -138,6 +139,7 @@ enum {
|
||||
#define NETIF_F_HW_L2FW_DOFFLOAD __NETIF_F(HW_L2FW_DOFFLOAD)
|
||||
#define NETIF_F_BUSY_POLL __NETIF_F(BUSY_POLL)
|
||||
#define NETIF_F_HW_TC __NETIF_F(HW_TC)
|
||||
+#define NETIF_F_HW_ACCEL_MQ __NETIF_F(HW_ACCEL_MQ)
|
||||
|
||||
/* Finds the next feature with the highest number of the range of start till 0.
|
||||
*/
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -1512,6 +1512,8 @@ enum netdev_priv_flags {
|
||||
* @if_port: Selectable AUI, TP, ...
|
||||
* @dma: DMA channel
|
||||
* @mtu: Interface MTU value
|
||||
+ * @min_mtu: Interface Minimum MTU value
|
||||
+ * @max_mtu: Interface Maximum MTU value
|
||||
* @type: Interface hardware type
|
||||
* @hard_header_len: Maximum hardware header length.
|
||||
* @min_header_len: Minimum hardware header length
|
||||
@@ -1743,6 +1745,8 @@ struct net_device {
|
||||
* and to use WRITE_ONCE() to annotate the writes.
|
||||
*/
|
||||
unsigned int mtu;
|
||||
+ unsigned int min_mtu;
|
||||
+ unsigned int max_mtu;
|
||||
unsigned short type;
|
||||
unsigned short hard_header_len;
|
||||
unsigned short min_header_len;
|
||||
--- a/include/linux/skbuff.h
|
||||
+++ b/include/linux/skbuff.h
|
||||
@@ -908,6 +908,7 @@ void kfree_skb(struct sk_buff *skb);
|
||||
void kfree_skb_list(struct sk_buff *segs);
|
||||
void skb_tx_error(struct sk_buff *skb);
|
||||
void consume_skb(struct sk_buff *skb);
|
||||
+void skb_recycle(struct sk_buff *skb);
|
||||
void __kfree_skb(struct sk_buff *skb);
|
||||
extern struct kmem_cache *skbuff_head_cache;
|
||||
|
||||
@@ -3095,6 +3096,7 @@ static inline void skb_free_datagram_loc
|
||||
}
|
||||
int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags);
|
||||
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);
|
||||
+void copy_skb_header(struct sk_buff *new, const struct sk_buff *old);
|
||||
int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len);
|
||||
__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to,
|
||||
int len, __wsum csum);
|
||||
--- a/include/linux/sys_soc.h
|
||||
+++ b/include/linux/sys_soc.h
|
||||
@@ -13,6 +13,7 @@ struct soc_device_attribute {
|
||||
const char *family;
|
||||
const char *revision;
|
||||
const char *soc_id;
|
||||
+ const void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -34,4 +35,6 @@ void soc_device_unregister(struct soc_de
|
||||
*/
|
||||
struct device *soc_device_to_device(struct soc_device *soc);
|
||||
|
||||
+const struct soc_device_attribute *soc_device_match(
|
||||
+ const struct soc_device_attribute *matches);
|
||||
#endif /* __SOC_BUS_H */
|
||||
--- a/include/uapi/linux/if_ether.h
|
||||
+++ b/include/uapi/linux/if_ether.h
|
||||
@@ -35,6 +35,7 @@
|
||||
#define ETH_DATA_LEN 1500 /* Max. octets in payload */
|
||||
#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
|
||||
#define ETH_FCS_LEN 4 /* Octets in the FCS */
|
||||
+#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */
|
||||
|
||||
/*
|
||||
* These are the defined Ethernet Protocol ID's.
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -6657,9 +6657,18 @@ int dev_set_mtu(struct net_device *dev,
|
||||
if (new_mtu == dev->mtu)
|
||||
return 0;
|
||||
|
||||
- /* MTU must be positive. */
|
||||
- if (new_mtu < 0)
|
||||
+ /* MTU must be positive, and in range */
|
||||
+ if (new_mtu < 0 || new_mtu < dev->min_mtu) {
|
||||
+ net_err_ratelimited("%s: Invalid MTU %d requested, hw min %d\n",
|
||||
+ dev->name, new_mtu, dev->min_mtu);
|
||||
return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (dev->max_mtu > 0 && new_mtu > dev->max_mtu) {
|
||||
+ net_err_ratelimited("%s: Invalid MTU %d requested, hw max %d\n",
|
||||
+ dev->name, new_mtu, dev->min_mtu);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
|
||||
if (!netif_device_present(dev))
|
||||
return -ENODEV;
|
||||
--- a/net/core/skbuff.c
|
||||
+++ b/net/core/skbuff.c
|
||||
@@ -846,6 +846,32 @@ void napi_consume_skb(struct sk_buff *sk
|
||||
}
|
||||
EXPORT_SYMBOL(napi_consume_skb);
|
||||
|
||||
+/**
|
||||
+ * skb_recycle - clean up an skb for reuse
|
||||
+ * @skb: buffer
|
||||
+ *
|
||||
+ * Recycles the skb to be reused as a receive buffer. This
|
||||
+ * function does any necessary reference count dropping, and
|
||||
+ * cleans up the skbuff as if it just came from __alloc_skb().
|
||||
+ */
|
||||
+void skb_recycle(struct sk_buff *skb)
|
||||
+{
|
||||
+ struct skb_shared_info *shinfo;
|
||||
+ u8 head_frag = skb->head_frag;
|
||||
+
|
||||
+ skb_release_head_state(skb);
|
||||
+
|
||||
+ shinfo = skb_shinfo(skb);
|
||||
+ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
|
||||
+ atomic_set(&shinfo->dataref, 1);
|
||||
+
|
||||
+ memset(skb, 0, offsetof(struct sk_buff, tail));
|
||||
+ skb->data = skb->head + NET_SKB_PAD;
|
||||
+ skb->head_frag = head_frag;
|
||||
+ skb_reset_tail_pointer(skb);
|
||||
+}
|
||||
+EXPORT_SYMBOL(skb_recycle);
|
||||
+
|
||||
/* Make sure a field is enclosed inside headers_start/headers_end section */
|
||||
#define CHECK_SKB_FIELD(field) \
|
||||
BUILD_BUG_ON(offsetof(struct sk_buff, field) < \
|
||||
@@ -1079,7 +1105,7 @@ static void skb_headers_offset_update(st
|
||||
skb->inner_mac_header += off;
|
||||
}
|
||||
|
||||
-static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
|
||||
+void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
|
||||
{
|
||||
__copy_skb_header(new, old);
|
||||
|
||||
@@ -1087,6 +1113,7 @@ static void copy_skb_header(struct sk_bu
|
||||
skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
|
||||
skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
|
||||
}
|
||||
+EXPORT_SYMBOL(copy_skb_header);
|
||||
|
||||
static inline int skb_alloc_rx_flag(const struct sk_buff *skb)
|
||||
{
|
||||
--- a/net/sched/sch_generic.c
|
||||
+++ b/net/sched/sch_generic.c
|
||||
@@ -309,6 +309,13 @@ static void dev_watchdog(unsigned long a
|
||||
txq->trans_timeout++;
|
||||
break;
|
||||
}
|
||||
+
|
||||
+ /* Devices with HW_ACCEL_MQ have multiple txqs
|
||||
+ * but update only the first one's transmission
|
||||
+ * timestamp so avoid checking the rest.
|
||||
+ */
|
||||
+ if (dev->features & NETIF_F_HW_ACCEL_MQ)
|
||||
+ break;
|
||||
}
|
||||
|
||||
if (some_queue_timedout) {
|
||||
@@ -0,0 +1,463 @@
|
||||
From 45e934873f9147f692dddbb61abc088f4c8059d7 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 14:51:29 +0800
|
||||
Subject: [PATCH 03/30] arch: support layerscape
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This is an integrated patch for layerscape arch support.
|
||||
|
||||
Signed-off-by: Madalin Bucur <madalin.bucur@nxp.com>
|
||||
Signed-off-by: Nipun Gupta <nipun.gupta@nxp.com>
|
||||
Signed-off-by: Zhao Qiang <B45475@freescale.com>
|
||||
Signed-off-by: Camelia Groza <camelia.groza@nxp.com>
|
||||
Signed-off-by: Haiying Wang <Haiying.wang@freescale.com>
|
||||
Signed-off-by: Pan Jiafei <Jiafei.Pan@nxp.com>
|
||||
Signed-off-by: Po Liu <po.liu@nxp.com>
|
||||
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com>
|
||||
Signed-off-by: Jianhua Xie <jianhua.xie@nxp.com>
|
||||
Signed-off-by: Horia Geantă <horia.geanta@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
arch/arm/include/asm/delay.h | 16 +++++++++
|
||||
arch/arm/include/asm/io.h | 31 ++++++++++++++++++
|
||||
arch/arm/include/asm/mach/map.h | 4 +--
|
||||
arch/arm/include/asm/pgtable.h | 7 ++++
|
||||
arch/arm/kernel/bios32.c | 43 ++++++++++++++++++++++++
|
||||
arch/arm/mm/dma-mapping.c | 1 +
|
||||
arch/arm/mm/ioremap.c | 7 ++++
|
||||
arch/arm/mm/mmu.c | 9 +++++
|
||||
arch/arm64/include/asm/cache.h | 2 +-
|
||||
arch/arm64/include/asm/io.h | 30 +++++++++++++++++
|
||||
arch/arm64/include/asm/pci.h | 4 +++
|
||||
arch/arm64/include/asm/pgtable-prot.h | 1 +
|
||||
arch/arm64/include/asm/pgtable.h | 5 +++
|
||||
arch/arm64/kernel/pci.c | 62 +++++++++++++++++++++++++++++++++++
|
||||
arch/arm64/mm/dma-mapping.c | 6 ++++
|
||||
15 files changed, 225 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/arch/arm/include/asm/delay.h
|
||||
+++ b/arch/arm/include/asm/delay.h
|
||||
@@ -57,6 +57,22 @@ extern void __bad_udelay(void);
|
||||
__const_udelay((n) * UDELAY_MULT)) : \
|
||||
__udelay(n))
|
||||
|
||||
+#define spin_event_timeout(condition, timeout, delay) \
|
||||
+({ \
|
||||
+ typeof(condition) __ret; \
|
||||
+ int i = 0; \
|
||||
+ while (!(__ret = (condition)) && (i++ < timeout)) { \
|
||||
+ if (delay) \
|
||||
+ udelay(delay); \
|
||||
+ else \
|
||||
+ cpu_relax(); \
|
||||
+ udelay(1); \
|
||||
+ } \
|
||||
+ if (!__ret) \
|
||||
+ __ret = (condition); \
|
||||
+ __ret; \
|
||||
+})
|
||||
+
|
||||
/* Loop-based definitions for assembly code. */
|
||||
extern void __loop_delay(unsigned long loops);
|
||||
extern void __loop_udelay(unsigned long usecs);
|
||||
--- a/arch/arm/include/asm/io.h
|
||||
+++ b/arch/arm/include/asm/io.h
|
||||
@@ -129,6 +129,7 @@ static inline u32 __raw_readl(const vola
|
||||
#define MT_DEVICE_NONSHARED 1
|
||||
#define MT_DEVICE_CACHED 2
|
||||
#define MT_DEVICE_WC 3
|
||||
+#define MT_MEMORY_RW_NS 4
|
||||
/*
|
||||
* types 4 onwards can be found in asm/mach/map.h and are undefined
|
||||
* for ioremap
|
||||
@@ -220,6 +221,34 @@ extern int pci_ioremap_io(unsigned int o
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+/* access ports */
|
||||
+#define setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr))
|
||||
+#define clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr))
|
||||
+
|
||||
+#define setbits16(_addr, _v) iowrite16be(ioread16be(_addr) | (_v), (_addr))
|
||||
+#define clrbits16(_addr, _v) iowrite16be(ioread16be(_addr) & ~(_v), (_addr))
|
||||
+
|
||||
+#define setbits8(_addr, _v) iowrite8(ioread8(_addr) | (_v), (_addr))
|
||||
+#define clrbits8(_addr, _v) iowrite8(ioread8(_addr) & ~(_v), (_addr))
|
||||
+
|
||||
+/* Clear and set bits in one shot. These macros can be used to clear and
|
||||
+ * set multiple bits in a register using a single read-modify-write. These
|
||||
+ * macros can also be used to set a multiple-bit bit pattern using a mask,
|
||||
+ * by specifying the mask in the 'clear' parameter and the new bit pattern
|
||||
+ * in the 'set' parameter.
|
||||
+ */
|
||||
+
|
||||
+#define clrsetbits_be32(addr, clear, set) \
|
||||
+ iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_le32(addr, clear, set) \
|
||||
+ iowrite32le((ioread32le(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_be16(addr, clear, set) \
|
||||
+ iowrite16be((ioread16be(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_le16(addr, clear, set) \
|
||||
+ iowrite16le((ioread16le(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_8(addr, clear, set) \
|
||||
+ iowrite8((ioread8(addr) & ~(clear)) | (set), (addr))
|
||||
+
|
||||
/*
|
||||
* IO port access primitives
|
||||
* -------------------------
|
||||
@@ -408,6 +437,8 @@ void __iomem *ioremap_wc(resource_size_t
|
||||
#define ioremap_wc ioremap_wc
|
||||
#define ioremap_wt ioremap_wc
|
||||
|
||||
+void __iomem *ioremap_cache_ns(resource_size_t res_cookie, size_t size);
|
||||
+
|
||||
void iounmap(volatile void __iomem *iomem_cookie);
|
||||
#define iounmap iounmap
|
||||
|
||||
--- a/arch/arm/include/asm/mach/map.h
|
||||
+++ b/arch/arm/include/asm/mach/map.h
|
||||
@@ -21,9 +21,9 @@ struct map_desc {
|
||||
unsigned int type;
|
||||
};
|
||||
|
||||
-/* types 0-3 are defined in asm/io.h */
|
||||
+/* types 0-4 are defined in asm/io.h */
|
||||
enum {
|
||||
- MT_UNCACHED = 4,
|
||||
+ MT_UNCACHED = 5,
|
||||
MT_CACHECLEAN,
|
||||
MT_MINICLEAN,
|
||||
MT_LOW_VECTORS,
|
||||
--- a/arch/arm/include/asm/pgtable.h
|
||||
+++ b/arch/arm/include/asm/pgtable.h
|
||||
@@ -118,6 +118,13 @@ extern pgprot_t pgprot_s2_device;
|
||||
#define pgprot_noncached(prot) \
|
||||
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)
|
||||
|
||||
+#define pgprot_cached(prot) \
|
||||
+ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_CACHED)
|
||||
+
|
||||
+#define pgprot_cached_ns(prot) \
|
||||
+ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_CACHED | \
|
||||
+ L_PTE_MT_DEV_NONSHARED)
|
||||
+
|
||||
#define pgprot_writecombine(prot) \
|
||||
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)
|
||||
|
||||
--- a/arch/arm/kernel/bios32.c
|
||||
+++ b/arch/arm/kernel/bios32.c
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/pcieport_if.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/map.h>
|
||||
@@ -64,6 +66,47 @@ void pcibios_report_status(u_int status_
|
||||
}
|
||||
|
||||
/*
|
||||
+ * Check device tree if the service interrupts are there
|
||||
+ */
|
||||
+int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
|
||||
+{
|
||||
+ int ret, count = 0;
|
||||
+ struct device_node *np = NULL;
|
||||
+
|
||||
+ if (dev->bus->dev.of_node)
|
||||
+ np = dev->bus->dev.of_node;
|
||||
+
|
||||
+ if (np == NULL)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (!IS_ENABLED(CONFIG_OF_IRQ))
|
||||
+ return 0;
|
||||
+
|
||||
+ /* If root port doesn't support MSI/MSI-X/INTx in RC mode,
|
||||
+ * request irq for aer
|
||||
+ */
|
||||
+ if (mask & PCIE_PORT_SERVICE_AER) {
|
||||
+ ret = of_irq_get_byname(np, "aer");
|
||||
+ if (ret > 0) {
|
||||
+ irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret;
|
||||
+ count++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (mask & PCIE_PORT_SERVICE_PME) {
|
||||
+ ret = of_irq_get_byname(np, "pme");
|
||||
+ if (ret > 0) {
|
||||
+ irqs[PCIE_PORT_SERVICE_PME_SHIFT] = ret;
|
||||
+ count++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* TODO: add more service interrupts if there it is in the device tree*/
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* We don't use this to fix the device, but initialisation of it.
|
||||
* It's not the correct use for this, but it works.
|
||||
* Note that the arbiter/ISA bridge appears to be buggy, specifically in
|
||||
--- a/arch/arm/mm/dma-mapping.c
|
||||
+++ b/arch/arm/mm/dma-mapping.c
|
||||
@@ -2410,6 +2410,7 @@ void arch_setup_dma_ops(struct device *d
|
||||
|
||||
set_dma_ops(dev, dma_ops);
|
||||
}
|
||||
+EXPORT_SYMBOL(arch_setup_dma_ops);
|
||||
|
||||
void arch_teardown_dma_ops(struct device *dev)
|
||||
{
|
||||
--- a/arch/arm/mm/ioremap.c
|
||||
+++ b/arch/arm/mm/ioremap.c
|
||||
@@ -398,6 +398,13 @@ void __iomem *ioremap_wc(resource_size_t
|
||||
}
|
||||
EXPORT_SYMBOL(ioremap_wc);
|
||||
|
||||
+void __iomem *ioremap_cache_ns(resource_size_t res_cookie, size_t size)
|
||||
+{
|
||||
+ return arch_ioremap_caller(res_cookie, size, MT_MEMORY_RW_NS,
|
||||
+ __builtin_return_address(0));
|
||||
+}
|
||||
+EXPORT_SYMBOL(ioremap_cache_ns);
|
||||
+
|
||||
/*
|
||||
* Remap an arbitrary physical address space into the kernel virtual
|
||||
* address space as memory. Needed when the kernel wants to execute
|
||||
--- a/arch/arm/mm/mmu.c
|
||||
+++ b/arch/arm/mm/mmu.c
|
||||
@@ -313,6 +313,13 @@ static struct mem_type mem_types[] __ro_
|
||||
.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
|
||||
.domain = DOMAIN_KERNEL,
|
||||
},
|
||||
+ [MT_MEMORY_RW_NS] = {
|
||||
+ .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
|
||||
+ L_PTE_XN,
|
||||
+ .prot_l1 = PMD_TYPE_TABLE,
|
||||
+ .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_XN,
|
||||
+ .domain = DOMAIN_KERNEL,
|
||||
+ },
|
||||
[MT_ROM] = {
|
||||
.prot_sect = PMD_TYPE_SECT,
|
||||
.domain = DOMAIN_KERNEL,
|
||||
@@ -644,6 +651,7 @@ static void __init build_mem_type_table(
|
||||
}
|
||||
kern_pgprot |= PTE_EXT_AF;
|
||||
vecs_pgprot |= PTE_EXT_AF;
|
||||
+ mem_types[MT_MEMORY_RW_NS].prot_pte |= PTE_EXT_AF | cp->pte;
|
||||
|
||||
/*
|
||||
* Set PXN for user mappings
|
||||
@@ -672,6 +680,7 @@ static void __init build_mem_type_table(
|
||||
mem_types[MT_MEMORY_RWX].prot_pte |= kern_pgprot;
|
||||
mem_types[MT_MEMORY_RW].prot_sect |= ecc_mask | cp->pmd;
|
||||
mem_types[MT_MEMORY_RW].prot_pte |= kern_pgprot;
|
||||
+ mem_types[MT_MEMORY_RW_NS].prot_sect |= ecc_mask | cp->pmd;
|
||||
mem_types[MT_MEMORY_DMA_READY].prot_pte |= kern_pgprot;
|
||||
mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= ecc_mask;
|
||||
mem_types[MT_ROM].prot_sect |= cp->pmd;
|
||||
--- a/arch/arm64/include/asm/cache.h
|
||||
+++ b/arch/arm64/include/asm/cache.h
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <asm/cachetype.h>
|
||||
|
||||
-#define L1_CACHE_SHIFT 7
|
||||
+#define L1_CACHE_SHIFT 6
|
||||
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
|
||||
|
||||
/*
|
||||
--- a/arch/arm64/include/asm/io.h
|
||||
+++ b/arch/arm64/include/asm/io.h
|
||||
@@ -171,6 +171,8 @@ extern void __iomem *ioremap_cache(phys_
|
||||
#define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
|
||||
#define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
|
||||
#define ioremap_wt(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
|
||||
+#define ioremap_cache_ns(addr, size) __ioremap((addr), (size), \
|
||||
+ __pgprot(PROT_NORMAL_NS))
|
||||
#define iounmap __iounmap
|
||||
|
||||
/*
|
||||
@@ -184,6 +186,34 @@ extern void __iomem *ioremap_cache(phys_
|
||||
#define iowrite32be(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_be32(v), p); })
|
||||
#define iowrite64be(v,p) ({ __iowmb(); __raw_writeq((__force __u64)cpu_to_be64(v), p); })
|
||||
|
||||
+/* access ports */
|
||||
+#define setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr))
|
||||
+#define clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr))
|
||||
+
|
||||
+#define setbits16(_addr, _v) iowrite16be(ioread16be(_addr) | (_v), (_addr))
|
||||
+#define clrbits16(_addr, _v) iowrite16be(ioread16be(_addr) & ~(_v), (_addr))
|
||||
+
|
||||
+#define setbits8(_addr, _v) iowrite8(ioread8(_addr) | (_v), (_addr))
|
||||
+#define clrbits8(_addr, _v) iowrite8(ioread8(_addr) & ~(_v), (_addr))
|
||||
+
|
||||
+/* Clear and set bits in one shot. These macros can be used to clear and
|
||||
+ * set multiple bits in a register using a single read-modify-write. These
|
||||
+ * macros can also be used to set a multiple-bit bit pattern using a mask,
|
||||
+ * by specifying the mask in the 'clear' parameter and the new bit pattern
|
||||
+ * in the 'set' parameter.
|
||||
+ */
|
||||
+
|
||||
+#define clrsetbits_be32(addr, clear, set) \
|
||||
+ iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_le32(addr, clear, set) \
|
||||
+ iowrite32le((ioread32le(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_be16(addr, clear, set) \
|
||||
+ iowrite16be((ioread16be(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_le16(addr, clear, set) \
|
||||
+ iowrite16le((ioread16le(addr) & ~(clear)) | (set), (addr))
|
||||
+#define clrsetbits_8(addr, clear, set) \
|
||||
+ iowrite8((ioread8(addr) & ~(clear)) | (set), (addr))
|
||||
+
|
||||
#include <asm-generic/io.h>
|
||||
|
||||
/*
|
||||
--- a/arch/arm64/include/asm/pci.h
|
||||
+++ b/arch/arm64/include/asm/pci.h
|
||||
@@ -31,6 +31,10 @@ static inline int pci_get_legacy_ide_irq
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
+#define HAVE_PCI_MMAP
|
||||
+extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
+ enum pci_mmap_state mmap_state,
|
||||
+ int write_combine);
|
||||
static inline int pci_proc_domain(struct pci_bus *bus)
|
||||
{
|
||||
return 1;
|
||||
--- a/arch/arm64/include/asm/pgtable-prot.h
|
||||
+++ b/arch/arm64/include/asm/pgtable-prot.h
|
||||
@@ -48,6 +48,7 @@
|
||||
#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
|
||||
#define PROT_NORMAL_WT (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
|
||||
#define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
|
||||
+#define PROT_NORMAL_NS (PTE_TYPE_PAGE | PTE_AF | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
|
||||
|
||||
#define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
|
||||
#define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
|
||||
--- a/arch/arm64/include/asm/pgtable.h
|
||||
+++ b/arch/arm64/include/asm/pgtable.h
|
||||
@@ -367,6 +367,11 @@ static inline int pmd_protnone(pmd_t pmd
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
|
||||
#define pgprot_writecombine(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
|
||||
+#define pgprot_cached(prot) \
|
||||
+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL) | \
|
||||
+ PTE_PXN | PTE_UXN)
|
||||
+#define pgprot_cached_ns(prot) \
|
||||
+ __pgprot(pgprot_val(pgprot_cached(prot)) ^ PTE_SHARED)
|
||||
#define pgprot_device(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
|
||||
#define __HAVE_PHYS_MEM_ACCESS_PROT
|
||||
--- a/arch/arm64/kernel/pci.c
|
||||
+++ b/arch/arm64/kernel/pci.c
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/pcieport_if.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
@@ -53,6 +55,66 @@ int pcibios_alloc_irq(struct pci_dev *de
|
||||
|
||||
return 0;
|
||||
}
|
||||
+
|
||||
+/*
|
||||
+ * Check device tree if the service interrupts are there
|
||||
+ */
|
||||
+int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
|
||||
+{
|
||||
+ int ret, count = 0;
|
||||
+ struct device_node *np = NULL;
|
||||
+
|
||||
+ if (dev->bus->dev.of_node)
|
||||
+ np = dev->bus->dev.of_node;
|
||||
+
|
||||
+ if (np == NULL)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (!IS_ENABLED(CONFIG_OF_IRQ))
|
||||
+ return 0;
|
||||
+
|
||||
+ /* If root port doesn't support MSI/MSI-X/INTx in RC mode,
|
||||
+ * request irq for aer
|
||||
+ */
|
||||
+ if (mask & PCIE_PORT_SERVICE_AER) {
|
||||
+ ret = of_irq_get_byname(np, "aer");
|
||||
+ if (ret > 0) {
|
||||
+ irqs[PCIE_PORT_SERVICE_AER_SHIFT] = ret;
|
||||
+ count++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (mask & PCIE_PORT_SERVICE_PME) {
|
||||
+ ret = of_irq_get_byname(np, "pme");
|
||||
+ if (ret > 0) {
|
||||
+ irqs[PCIE_PORT_SERVICE_PME_SHIFT] = ret;
|
||||
+ count++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* TODO: add more service interrupts if there it is in the device tree*/
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
+ enum pci_mmap_state mmap_state, int write_combine)
|
||||
+{
|
||||
+ if (mmap_state == pci_mmap_io)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /*
|
||||
+ * Mark this as IO
|
||||
+ */
|
||||
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
+
|
||||
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||||
+ vma->vm_end - vma->vm_start,
|
||||
+ vma->vm_page_prot))
|
||||
+ return -EAGAIN;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
/*
|
||||
* raw_pci_read/write - Platform-specific PCI config space access.
|
||||
--- a/arch/arm64/mm/dma-mapping.c
|
||||
+++ b/arch/arm64/mm/dma-mapping.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <linux/swiotlb.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
+#include <../../../drivers/staging/fsl-mc/include/mc-bus.h>
|
||||
|
||||
static int swiotlb __ro_after_init;
|
||||
|
||||
@@ -925,6 +926,10 @@ static int __init __iommu_dma_init(void)
|
||||
if (!ret)
|
||||
ret = register_iommu_dma_ops_notifier(&pci_bus_type);
|
||||
#endif
|
||||
+#ifdef CONFIG_FSL_MC_BUS
|
||||
+ if (!ret)
|
||||
+ ret = register_iommu_dma_ops_notifier(&fsl_mc_bus_type);
|
||||
+#endif
|
||||
return ret;
|
||||
}
|
||||
arch_initcall(__iommu_dma_init);
|
||||
@@ -978,3 +983,4 @@ void arch_setup_dma_ops(struct device *d
|
||||
dev->archdata.dma_coherent = coherent;
|
||||
__iommu_setup_dma_ops(dev, dma_base, size, iommu);
|
||||
}
|
||||
+EXPORT_SYMBOL(arch_setup_dma_ops);
|
||||
10423
target/linux/layerscape/patches-4.9/302-dts-support-layercape.patch
Normal file
10423
target/linux/layerscape/patches-4.9/302-dts-support-layercape.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,986 @@
|
||||
From 825d57369b196b64387348922b47adc5b651622c Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 14:55:47 +0800
|
||||
Subject: [PATCH 05/30] mtd: spi-nor: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape qspi support.
|
||||
|
||||
Signed-off-by: Suresh Gupta <suresh.gupta@nxp.com>
|
||||
Signed-off-by: Yunhui Cui <B56489@freescale.com>
|
||||
Signed-off-by: mar.krzeminski <mar.krzeminski@gmail.com>
|
||||
Signed-off-by: Alison Wang <b18965@freescale.com>
|
||||
Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.kw@hitachi.com>
|
||||
Signed-off-by: LABBE Corentin <clabbe.montjoie@gmail.com>
|
||||
Signed-off-by: Yuan Yao <yao.yuan@nxp.com>
|
||||
Signed-off-by: Alexander Kurz <akurz@blala.de>
|
||||
Signed-off-by: L. D. Pinney <ldpinney@gmail.com>
|
||||
Signed-off-by: Ash Benz <ash.benz@bk.ru>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/mtd/mtdchar.c | 2 +-
|
||||
drivers/mtd/spi-nor/fsl-quadspi.c | 327 +++++++++++++++++++++++++++++++-------
|
||||
drivers/mtd/spi-nor/spi-nor.c | 136 ++++++++++++++--
|
||||
include/linux/mtd/spi-nor.h | 14 +-
|
||||
4 files changed, 409 insertions(+), 70 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/mtdchar.c
|
||||
+++ b/drivers/mtd/mtdchar.c
|
||||
@@ -452,7 +452,7 @@ static int mtdchar_readoob(struct file *
|
||||
* data. For our userspace tools it is important to dump areas
|
||||
* with ECC errors!
|
||||
* For kernel internal usage it also might return -EUCLEAN
|
||||
- * to signal the caller that a bitflip has occured and has
|
||||
+ * to signal the caller that a bitflip has occurred and has
|
||||
* been corrected by the ECC algorithm.
|
||||
*
|
||||
* Note: currently the standard NAND function, nand_read_oob_std,
|
||||
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
@@ -41,6 +41,8 @@
|
||||
#define QUADSPI_QUIRK_TKT253890 (1 << 2)
|
||||
/* Controller cannot wake up from wait mode, TKT245618 */
|
||||
#define QUADSPI_QUIRK_TKT245618 (1 << 3)
|
||||
+/* QSPI_AMBA_BASE is internally added by SOC design */
|
||||
+#define QUADSPI_AMBA_BASE_INTERNAL (0x10000)
|
||||
|
||||
/* The registers */
|
||||
#define QUADSPI_MCR 0x00
|
||||
@@ -193,7 +195,7 @@
|
||||
#define QUADSPI_LUT_NUM 64
|
||||
|
||||
/* SEQID -- we can have 16 seqids at most. */
|
||||
-#define SEQID_QUAD_READ 0
|
||||
+#define SEQID_READ 0
|
||||
#define SEQID_WREN 1
|
||||
#define SEQID_WRDI 2
|
||||
#define SEQID_RDSR 3
|
||||
@@ -205,15 +207,22 @@
|
||||
#define SEQID_RDCR 9
|
||||
#define SEQID_EN4B 10
|
||||
#define SEQID_BRWR 11
|
||||
+#define SEQID_RDAR_OR_RD_EVCR 12
|
||||
+#define SEQID_WRAR 13
|
||||
+#define SEQID_WD_EVCR 14
|
||||
|
||||
#define QUADSPI_MIN_IOMAP SZ_4M
|
||||
|
||||
+#define FLASH_VENDOR_SPANSION_FS "s25fs"
|
||||
+#define SPANSION_S25FS_FAMILY (1 << 1)
|
||||
+
|
||||
enum fsl_qspi_devtype {
|
||||
FSL_QUADSPI_VYBRID,
|
||||
FSL_QUADSPI_IMX6SX,
|
||||
FSL_QUADSPI_IMX7D,
|
||||
FSL_QUADSPI_IMX6UL,
|
||||
FSL_QUADSPI_LS1021A,
|
||||
+ FSL_QUADSPI_LS2080A,
|
||||
};
|
||||
|
||||
struct fsl_qspi_devtype_data {
|
||||
@@ -224,7 +233,7 @@ struct fsl_qspi_devtype_data {
|
||||
int driver_data;
|
||||
};
|
||||
|
||||
-static struct fsl_qspi_devtype_data vybrid_data = {
|
||||
+static const struct fsl_qspi_devtype_data vybrid_data = {
|
||||
.devtype = FSL_QUADSPI_VYBRID,
|
||||
.rxfifo = 128,
|
||||
.txfifo = 64,
|
||||
@@ -232,7 +241,7 @@ static struct fsl_qspi_devtype_data vybr
|
||||
.driver_data = QUADSPI_QUIRK_SWAP_ENDIAN,
|
||||
};
|
||||
|
||||
-static struct fsl_qspi_devtype_data imx6sx_data = {
|
||||
+static const struct fsl_qspi_devtype_data imx6sx_data = {
|
||||
.devtype = FSL_QUADSPI_IMX6SX,
|
||||
.rxfifo = 128,
|
||||
.txfifo = 512,
|
||||
@@ -241,7 +250,7 @@ static struct fsl_qspi_devtype_data imx6
|
||||
| QUADSPI_QUIRK_TKT245618,
|
||||
};
|
||||
|
||||
-static struct fsl_qspi_devtype_data imx7d_data = {
|
||||
+static const struct fsl_qspi_devtype_data imx7d_data = {
|
||||
.devtype = FSL_QUADSPI_IMX7D,
|
||||
.rxfifo = 512,
|
||||
.txfifo = 512,
|
||||
@@ -250,7 +259,7 @@ static struct fsl_qspi_devtype_data imx7
|
||||
| QUADSPI_QUIRK_4X_INT_CLK,
|
||||
};
|
||||
|
||||
-static struct fsl_qspi_devtype_data imx6ul_data = {
|
||||
+static const struct fsl_qspi_devtype_data imx6ul_data = {
|
||||
.devtype = FSL_QUADSPI_IMX6UL,
|
||||
.rxfifo = 128,
|
||||
.txfifo = 512,
|
||||
@@ -267,6 +276,14 @@ static struct fsl_qspi_devtype_data ls10
|
||||
.driver_data = 0,
|
||||
};
|
||||
|
||||
+static struct fsl_qspi_devtype_data ls2080a_data = {
|
||||
+ .devtype = FSL_QUADSPI_LS2080A,
|
||||
+ .rxfifo = 128,
|
||||
+ .txfifo = 64,
|
||||
+ .ahb_buf_size = 1024,
|
||||
+ .driver_data = QUADSPI_AMBA_BASE_INTERNAL | QUADSPI_QUIRK_TKT253890,
|
||||
+};
|
||||
+
|
||||
#define FSL_QSPI_MAX_CHIP 4
|
||||
struct fsl_qspi {
|
||||
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
||||
@@ -282,6 +299,7 @@ struct fsl_qspi {
|
||||
u32 nor_size;
|
||||
u32 nor_num;
|
||||
u32 clk_rate;
|
||||
+ u32 ddr_smp;
|
||||
unsigned int chip_base_addr; /* We may support two chips. */
|
||||
bool has_second_chip;
|
||||
bool big_endian;
|
||||
@@ -309,6 +327,23 @@ static inline int needs_wakeup_wait_mode
|
||||
return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
|
||||
}
|
||||
|
||||
+static inline int has_added_amba_base_internal(struct fsl_qspi *q)
|
||||
+{
|
||||
+ return q->devtype_data->driver_data & QUADSPI_AMBA_BASE_INTERNAL;
|
||||
+}
|
||||
+
|
||||
+static u32 fsl_get_nor_vendor(struct spi_nor *nor)
|
||||
+{
|
||||
+ u32 vendor_id;
|
||||
+
|
||||
+ if (nor->vendor) {
|
||||
+ if (memcmp(nor->vendor, FLASH_VENDOR_SPANSION_FS,
|
||||
+ sizeof(FLASH_VENDOR_SPANSION_FS) - 1))
|
||||
+ vendor_id = SPANSION_S25FS_FAMILY;
|
||||
+ }
|
||||
+ return vendor_id;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* R/W functions for big- or little-endian registers:
|
||||
* The qSPI controller's endian is independent of the CPU core's endian.
|
||||
@@ -331,6 +366,31 @@ static u32 qspi_readl(struct fsl_qspi *q
|
||||
return ioread32(addr);
|
||||
}
|
||||
|
||||
+static inline u32 *u8tou32(u32 *dest, const u8 *src, size_t n)
|
||||
+{
|
||||
+ size_t i;
|
||||
+ *dest = 0;
|
||||
+
|
||||
+ n = n > 4 ? 4 : n;
|
||||
+ for (i = 0; i < n; i++)
|
||||
+ *dest |= *src++ << i * 8;
|
||||
+
|
||||
+ return dest;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+static inline u8 *u32tou8(u8 *dest, const u32 *src, size_t n)
|
||||
+{
|
||||
+ size_t i;
|
||||
+ u8 *xdest = dest;
|
||||
+
|
||||
+ n = n > 4 ? 4 : n;
|
||||
+ for (i = 0; i < n; i++)
|
||||
+ *xdest++ = *src >> i * 8;
|
||||
+
|
||||
+ return dest;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* An IC bug makes us to re-arrange the 32-bit data.
|
||||
* The following chips, such as IMX6SLX, have fixed this bug.
|
||||
@@ -373,8 +433,15 @@ static void fsl_qspi_init_lut(struct fsl
|
||||
void __iomem *base = q->iobase;
|
||||
int rxfifo = q->devtype_data->rxfifo;
|
||||
u32 lut_base;
|
||||
- u8 cmd, addrlen, dummy;
|
||||
int i;
|
||||
+ u32 vendor;
|
||||
+
|
||||
+ struct spi_nor *nor = &q->nor[0];
|
||||
+ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
|
||||
+ u8 read_op = nor->read_opcode;
|
||||
+ u8 read_dm = nor->read_dummy;
|
||||
+
|
||||
+ vendor = fsl_get_nor_vendor(nor);
|
||||
|
||||
fsl_qspi_unlock_lut(q);
|
||||
|
||||
@@ -382,24 +449,50 @@ static void fsl_qspi_init_lut(struct fsl
|
||||
for (i = 0; i < QUADSPI_LUT_NUM; i++)
|
||||
qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
|
||||
|
||||
- /* Quad Read */
|
||||
- lut_base = SEQID_QUAD_READ * 4;
|
||||
+ /* Read */
|
||||
+ lut_base = SEQID_READ * 4;
|
||||
|
||||
- if (q->nor_size <= SZ_16M) {
|
||||
- cmd = SPINOR_OP_READ_1_1_4;
|
||||
- addrlen = ADDR24BIT;
|
||||
- dummy = 8;
|
||||
- } else {
|
||||
- /* use the 4-byte address */
|
||||
- cmd = SPINOR_OP_READ_1_1_4;
|
||||
- addrlen = ADDR32BIT;
|
||||
- dummy = 8;
|
||||
- }
|
||||
-
|
||||
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
||||
+ if (nor->flash_read == SPI_NOR_FAST) {
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, read_op) |
|
||||
+ LUT1(ADDR, PAD1, addrlen),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+ qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
|
||||
+ LUT1(FSL_READ, PAD1, rxfifo),
|
||||
+ base + QUADSPI_LUT(lut_base + 1));
|
||||
+ } else if (nor->flash_read == SPI_NOR_QUAD) {
|
||||
+ if (q->nor_size == 0x4000000) {
|
||||
+ read_op = 0xEC;
|
||||
+ qspi_writel(q,
|
||||
+ LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD4, addrlen),
|
||||
base + QUADSPI_LUT(lut_base));
|
||||
- qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
|
||||
+ qspi_writel(q,
|
||||
+ LUT0(MODE, PAD4, 0xff) | LUT1(DUMMY, PAD4, read_dm),
|
||||
base + QUADSPI_LUT(lut_base + 1));
|
||||
+ qspi_writel(q,
|
||||
+ LUT0(FSL_READ, PAD4, rxfifo),
|
||||
+ base + QUADSPI_LUT(lut_base + 2));
|
||||
+ } else {
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, read_op) |
|
||||
+ LUT1(ADDR, PAD1, addrlen),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+ qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
|
||||
+ LUT1(FSL_READ, PAD4, rxfifo),
|
||||
+ base + QUADSPI_LUT(lut_base + 1));
|
||||
+ }
|
||||
+ } else if (nor->flash_read == SPI_NOR_DDR_QUAD) {
|
||||
+ /* read mode : 1-4-4, such as Spansion s25fl128s. */
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, read_op)
|
||||
+ | LUT1(ADDR_DDR, PAD4, addrlen),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+
|
||||
+ qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff)
|
||||
+ | LUT1(DUMMY, PAD1, read_dm),
|
||||
+ base + QUADSPI_LUT(lut_base + 1));
|
||||
+
|
||||
+ qspi_writel(q, LUT0(FSL_READ_DDR, PAD4, rxfifo)
|
||||
+ | LUT1(JMP_ON_CS, PAD1, 0),
|
||||
+ base + QUADSPI_LUT(lut_base + 2));
|
||||
+ }
|
||||
|
||||
/* Write enable */
|
||||
lut_base = SEQID_WREN * 4;
|
||||
@@ -409,16 +502,8 @@ static void fsl_qspi_init_lut(struct fsl
|
||||
/* Page Program */
|
||||
lut_base = SEQID_PP * 4;
|
||||
|
||||
- if (q->nor_size <= SZ_16M) {
|
||||
- cmd = SPINOR_OP_PP;
|
||||
- addrlen = ADDR24BIT;
|
||||
- } else {
|
||||
- /* use the 4-byte address */
|
||||
- cmd = SPINOR_OP_PP;
|
||||
- addrlen = ADDR32BIT;
|
||||
- }
|
||||
-
|
||||
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) |
|
||||
+ LUT1(ADDR, PAD1, addrlen),
|
||||
base + QUADSPI_LUT(lut_base));
|
||||
qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
|
||||
base + QUADSPI_LUT(lut_base + 1));
|
||||
@@ -432,10 +517,8 @@ static void fsl_qspi_init_lut(struct fsl
|
||||
/* Erase a sector */
|
||||
lut_base = SEQID_SE * 4;
|
||||
|
||||
- cmd = q->nor[0].erase_opcode;
|
||||
- addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
|
||||
-
|
||||
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) |
|
||||
+ LUT1(ADDR, PAD1, addrlen),
|
||||
base + QUADSPI_LUT(lut_base));
|
||||
|
||||
/* Erase the whole chip */
|
||||
@@ -476,6 +559,44 @@ static void fsl_qspi_init_lut(struct fsl
|
||||
qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
|
||||
base + QUADSPI_LUT(lut_base));
|
||||
|
||||
+
|
||||
+ /*
|
||||
+ * Flash Micron and Spansion command confilict
|
||||
+ * use the same value 0x65. But it indicates different meaning.
|
||||
+ */
|
||||
+ lut_base = SEQID_RDAR_OR_RD_EVCR * 4;
|
||||
+
|
||||
+ if (vendor == SPANSION_S25FS_FAMILY) {
|
||||
+ /*
|
||||
+ * Read any device register.
|
||||
+ * Used for Spansion S25FS-S family flash only.
|
||||
+ */
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_RDAR) |
|
||||
+ LUT1(ADDR, PAD1, ADDR24BIT),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+ qspi_writel(q, LUT0(DUMMY, PAD1, 8) | LUT1(FSL_READ, PAD1, 1),
|
||||
+ base + QUADSPI_LUT(lut_base + 1));
|
||||
+ } else {
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Write any device register.
|
||||
+ * Used for Spansion S25FS-S family flash only.
|
||||
+ */
|
||||
+ lut_base = SEQID_WRAR * 4;
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_WRAR) |
|
||||
+ LUT1(ADDR, PAD1, ADDR24BIT),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+ qspi_writel(q, LUT0(FSL_WRITE, PAD1, 1),
|
||||
+ base + QUADSPI_LUT(lut_base + 1));
|
||||
+
|
||||
+ /* Write EVCR register */
|
||||
+ lut_base = SEQID_WD_EVCR * 4;
|
||||
+ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR),
|
||||
+ base + QUADSPI_LUT(lut_base));
|
||||
+
|
||||
fsl_qspi_lock_lut(q);
|
||||
}
|
||||
|
||||
@@ -483,8 +604,24 @@ static void fsl_qspi_init_lut(struct fsl
|
||||
static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
+ case SPINOR_OP_READ_1_4_4_D:
|
||||
+ case SPINOR_OP_READ4_1_4_4_D:
|
||||
+ case SPINOR_OP_READ4_1_1_4:
|
||||
case SPINOR_OP_READ_1_1_4:
|
||||
- return SEQID_QUAD_READ;
|
||||
+ case SPINOR_OP_READ_FAST:
|
||||
+ case SPINOR_OP_READ4_FAST:
|
||||
+ return SEQID_READ;
|
||||
+ /*
|
||||
+ * Spansion & Micron use the same command value 0x65
|
||||
+ * Spansion: SPINOR_OP_SPANSION_RDAR, read any register.
|
||||
+ * Micron: SPINOR_OP_RD_EVCR,
|
||||
+ * read enhanced volatile configuration register.
|
||||
+ * case SPINOR_OP_RD_EVCR:
|
||||
+ */
|
||||
+ case SPINOR_OP_SPANSION_RDAR:
|
||||
+ return SEQID_RDAR_OR_RD_EVCR;
|
||||
+ case SPINOR_OP_SPANSION_WRAR:
|
||||
+ return SEQID_WRAR;
|
||||
case SPINOR_OP_WREN:
|
||||
return SEQID_WREN;
|
||||
case SPINOR_OP_WRDI:
|
||||
@@ -496,6 +633,7 @@ static int fsl_qspi_get_seqid(struct fsl
|
||||
case SPINOR_OP_CHIP_ERASE:
|
||||
return SEQID_CHIP_ERASE;
|
||||
case SPINOR_OP_PP:
|
||||
+ case SPINOR_OP_PP_4B:
|
||||
return SEQID_PP;
|
||||
case SPINOR_OP_RDID:
|
||||
return SEQID_RDID;
|
||||
@@ -507,6 +645,8 @@ static int fsl_qspi_get_seqid(struct fsl
|
||||
return SEQID_EN4B;
|
||||
case SPINOR_OP_BRWR:
|
||||
return SEQID_BRWR;
|
||||
+ case SPINOR_OP_WD_EVCR:
|
||||
+ return SEQID_WD_EVCR;
|
||||
default:
|
||||
if (cmd == q->nor[0].erase_opcode)
|
||||
return SEQID_SE;
|
||||
@@ -531,8 +671,11 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 c
|
||||
/* save the reg */
|
||||
reg = qspi_readl(q, base + QUADSPI_MCR);
|
||||
|
||||
- qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr,
|
||||
- base + QUADSPI_SFAR);
|
||||
+ if (has_added_amba_base_internal(q))
|
||||
+ qspi_writel(q, q->chip_base_addr + addr, base + QUADSPI_SFAR);
|
||||
+ else
|
||||
+ qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr,
|
||||
+ base + QUADSPI_SFAR);
|
||||
qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
|
||||
base + QUADSPI_RBCT);
|
||||
qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR);
|
||||
@@ -582,10 +725,10 @@ static void fsl_qspi_read_data(struct fs
|
||||
q->chip_base_addr, tmp);
|
||||
|
||||
if (len >= 4) {
|
||||
- *((u32 *)rxbuf) = tmp;
|
||||
+ u32tou8(rxbuf, &tmp, 4);
|
||||
rxbuf += 4;
|
||||
} else {
|
||||
- memcpy(rxbuf, &tmp, len);
|
||||
+ u32tou8(rxbuf, &tmp, len);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -619,11 +762,12 @@ static inline void fsl_qspi_invalid(stru
|
||||
}
|
||||
|
||||
static ssize_t fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
||||
- u8 opcode, unsigned int to, u32 *txbuf,
|
||||
+ u8 opcode, unsigned int to, u8 *txbuf,
|
||||
unsigned count)
|
||||
{
|
||||
int ret, i, j;
|
||||
u32 tmp;
|
||||
+ u8 byts;
|
||||
|
||||
dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n",
|
||||
q->chip_base_addr, to, count);
|
||||
@@ -633,10 +777,13 @@ static ssize_t fsl_qspi_nor_write(struct
|
||||
qspi_writel(q, tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR);
|
||||
|
||||
/* fill the TX data to the FIFO */
|
||||
+ byts = count;
|
||||
for (j = 0, i = ((count + 3) / 4); j < i; j++) {
|
||||
- tmp = fsl_qspi_endian_xchg(q, *txbuf);
|
||||
+ u8tou32(&tmp, txbuf, byts);
|
||||
+ tmp = fsl_qspi_endian_xchg(q, tmp);
|
||||
qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR);
|
||||
- txbuf++;
|
||||
+ txbuf += 4;
|
||||
+ byts -= 4;
|
||||
}
|
||||
|
||||
/* fill the TXFIFO upto 16 bytes for i.MX7d */
|
||||
@@ -657,11 +804,43 @@ static void fsl_qspi_set_map_addr(struct
|
||||
{
|
||||
int nor_size = q->nor_size;
|
||||
void __iomem *base = q->iobase;
|
||||
+ u32 mem_base;
|
||||
+
|
||||
+ if (has_added_amba_base_internal(q))
|
||||
+ mem_base = 0x0;
|
||||
+ else
|
||||
+ mem_base = q->memmap_phy;
|
||||
+
|
||||
+ qspi_writel(q, nor_size + mem_base, base + QUADSPI_SFA1AD);
|
||||
+ qspi_writel(q, nor_size * 2 + mem_base, base + QUADSPI_SFA2AD);
|
||||
+ qspi_writel(q, nor_size * 3 + mem_base, base + QUADSPI_SFB1AD);
|
||||
+ qspi_writel(q, nor_size * 4 + mem_base, base + QUADSPI_SFB2AD);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * enable controller ddr quad mode to support different
|
||||
+ * vender flashes ddr quad mode.
|
||||
+ */
|
||||
+static void set_ddr_quad_mode(struct fsl_qspi *q)
|
||||
+{
|
||||
+ u32 reg, reg2;
|
||||
+
|
||||
+ reg = qspi_readl(q, q->iobase + QUADSPI_MCR);
|
||||
+
|
||||
+ /* Firstly, disable the module */
|
||||
+ qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
|
||||
+
|
||||
+ /* Set the Sampling Register for DDR */
|
||||
+ reg2 = qspi_readl(q, q->iobase + QUADSPI_SMPR);
|
||||
+ reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK;
|
||||
+ reg2 |= (((q->ddr_smp) << QUADSPI_SMPR_DDRSMP_SHIFT) &
|
||||
+ QUADSPI_SMPR_DDRSMP_MASK);
|
||||
+ qspi_writel(q, reg2, q->iobase + QUADSPI_SMPR);
|
||||
+
|
||||
+ /* Enable the module again (enable the DDR too) */
|
||||
+ reg |= QUADSPI_MCR_DDR_EN_MASK;
|
||||
+ qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
|
||||
|
||||
- qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
|
||||
- qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
|
||||
- qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
|
||||
- qspi_writel(q, nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -704,6 +883,11 @@ static void fsl_qspi_init_abh_read(struc
|
||||
seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode);
|
||||
qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
|
||||
q->iobase + QUADSPI_BFGENCR);
|
||||
+
|
||||
+ /* enable the DDR quad read */
|
||||
+ if (q->nor->flash_read == SPI_NOR_DDR_QUAD)
|
||||
+ set_ddr_quad_mode(q);
|
||||
+
|
||||
}
|
||||
|
||||
/* This function was used to prepare and enable QSPI clock */
|
||||
@@ -822,6 +1006,7 @@ static const struct of_device_id fsl_qsp
|
||||
{ .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
|
||||
{ .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
|
||||
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
|
||||
+ { .compatible = "fsl,ls2080a-qspi", .data = (void *)&ls2080a_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
|
||||
@@ -835,8 +1020,12 @@ static int fsl_qspi_read_reg(struct spi_
|
||||
{
|
||||
int ret;
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
+ u32 to = 0;
|
||||
|
||||
- ret = fsl_qspi_runcmd(q, opcode, 0, len);
|
||||
+ if (opcode == SPINOR_OP_SPANSION_RDAR)
|
||||
+ u8tou32(&to, nor->cmd_buf, 4);
|
||||
+
|
||||
+ ret = fsl_qspi_runcmd(q, opcode, to, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -848,9 +1037,13 @@ static int fsl_qspi_write_reg(struct spi
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
int ret;
|
||||
+ u32 to = 0;
|
||||
+
|
||||
+ if (opcode == SPINOR_OP_SPANSION_WRAR)
|
||||
+ u8tou32(&to, nor->cmd_buf, 4);
|
||||
|
||||
if (!buf) {
|
||||
- ret = fsl_qspi_runcmd(q, opcode, 0, 1);
|
||||
+ ret = fsl_qspi_runcmd(q, opcode, to, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -859,7 +1052,7 @@ static int fsl_qspi_write_reg(struct spi
|
||||
|
||||
} else if (len > 0) {
|
||||
ret = fsl_qspi_nor_write(q, nor, opcode, 0,
|
||||
- (u32 *)buf, len);
|
||||
+ buf, len);
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
} else {
|
||||
@@ -875,7 +1068,7 @@ static ssize_t fsl_qspi_write(struct spi
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
|
||||
- (u32 *)buf, len);
|
||||
+ (u8 *)buf, len);
|
||||
|
||||
/* invalid the data in the AHB buffer. */
|
||||
fsl_qspi_invalid(q);
|
||||
@@ -922,7 +1115,7 @@ static ssize_t fsl_qspi_read(struct spi_
|
||||
len);
|
||||
|
||||
/* Read out the data directly from the AHB buffer.*/
|
||||
- memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
||||
+ memcpy_toio(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
||||
len);
|
||||
|
||||
return len;
|
||||
@@ -980,6 +1173,8 @@ static int fsl_qspi_probe(struct platfor
|
||||
struct spi_nor *nor;
|
||||
struct mtd_info *mtd;
|
||||
int ret, i = 0;
|
||||
+ int find_node;
|
||||
+ enum read_mode mode = SPI_NOR_QUAD;
|
||||
|
||||
q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
|
||||
if (!q)
|
||||
@@ -1027,6 +1222,12 @@ static int fsl_qspi_probe(struct platfor
|
||||
goto clk_failed;
|
||||
}
|
||||
|
||||
+ /* find ddrsmp value */
|
||||
+ ret = of_property_read_u32(dev->of_node, "fsl,ddr-sampling-point",
|
||||
+ &q->ddr_smp);
|
||||
+ if (ret)
|
||||
+ q->ddr_smp = 0;
|
||||
+
|
||||
/* find the irq */
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
@@ -1050,6 +1251,7 @@ static int fsl_qspi_probe(struct platfor
|
||||
|
||||
mutex_init(&q->lock);
|
||||
|
||||
+ find_node = 0;
|
||||
/* iterate the subnodes. */
|
||||
for_each_available_child_of_node(dev->of_node, np) {
|
||||
/* skip the holes */
|
||||
@@ -1076,18 +1278,25 @@ static int fsl_qspi_probe(struct platfor
|
||||
ret = of_property_read_u32(np, "spi-max-frequency",
|
||||
&q->clk_rate);
|
||||
if (ret < 0)
|
||||
- goto mutex_failed;
|
||||
+ continue;
|
||||
|
||||
/* set the chip address for READID */
|
||||
fsl_qspi_set_base_addr(q, nor);
|
||||
|
||||
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
|
||||
+ ret = of_property_read_bool(np, "m25p,fast-read");
|
||||
+ mode = (ret) ? SPI_NOR_FAST : SPI_NOR_QUAD;
|
||||
+ /* Can we enable the DDR Quad Read? */
|
||||
+ ret = of_property_read_bool(np, "ddr-quad-read");
|
||||
if (ret)
|
||||
- goto mutex_failed;
|
||||
+ mode = SPI_NOR_DDR_QUAD;
|
||||
+
|
||||
+ ret = spi_nor_scan(nor, NULL, mode);
|
||||
+ if (ret)
|
||||
+ continue;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
- goto mutex_failed;
|
||||
+ continue;
|
||||
|
||||
/* Set the correct NOR size now. */
|
||||
if (q->nor_size == 0) {
|
||||
@@ -1110,8 +1319,12 @@ static int fsl_qspi_probe(struct platfor
|
||||
nor->page_size = q->devtype_data->txfifo;
|
||||
|
||||
i++;
|
||||
+ find_node++;
|
||||
}
|
||||
|
||||
+ if (find_node == 0)
|
||||
+ goto mutex_failed;
|
||||
+
|
||||
/* finish the rest init. */
|
||||
ret = fsl_qspi_nor_setup_last(q);
|
||||
if (ret)
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -40,6 +40,13 @@
|
||||
#define SPI_NOR_MAX_ID_LEN 6
|
||||
#define SPI_NOR_MAX_ADDR_WIDTH 4
|
||||
|
||||
+#define SPI_NOR_MICRON_WRITE_ENABLE 0x7f
|
||||
+/* Added for S25FS-S family flash */
|
||||
+#define SPINOR_CONFIG_REG3_OFFSET 0x800004
|
||||
+#define CR3V_4KB_ERASE_UNABLE 0x8
|
||||
+#define SPINOR_S25FS_FAMILY_ID 0x81
|
||||
+
|
||||
+
|
||||
struct flash_info {
|
||||
char *name;
|
||||
|
||||
@@ -68,7 +75,8 @@ struct flash_info {
|
||||
#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
|
||||
#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
|
||||
#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
|
||||
-#define USE_FSR BIT(7) /* use flag status register */
|
||||
+#define USE_FSR BIT(13) /* use flag status register */
|
||||
+#define SPI_NOR_DDR_QUAD_READ BIT(7) /* Flash supports DDR Quad Read */
|
||||
#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
|
||||
#define SPI_NOR_HAS_TB BIT(9) /*
|
||||
* Flash SR has Top/Bottom (TB) protect
|
||||
@@ -85,9 +93,11 @@ struct flash_info {
|
||||
* Use dedicated 4byte address op codes
|
||||
* to support memory size above 128Mib.
|
||||
*/
|
||||
+#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
|
||||
};
|
||||
|
||||
#define JEDEC_MFR(info) ((info)->id[0])
|
||||
+#define EXT_ID(info) ((info)->id[5])
|
||||
|
||||
static const struct flash_info *spi_nor_match_id(const char *name);
|
||||
|
||||
@@ -132,7 +142,7 @@ static int read_fsr(struct spi_nor *nor)
|
||||
/*
|
||||
* Read configuration register, returning its value in the
|
||||
* location. Return the configuration register value.
|
||||
- * Returns negative if error occured.
|
||||
+ * Returns negative if error occurred.
|
||||
*/
|
||||
static int read_cr(struct spi_nor *nor)
|
||||
{
|
||||
@@ -160,6 +170,8 @@ static inline int spi_nor_read_dummy_cyc
|
||||
case SPI_NOR_DUAL:
|
||||
case SPI_NOR_QUAD:
|
||||
return 8;
|
||||
+ case SPI_NOR_DDR_QUAD:
|
||||
+ return 6;
|
||||
case SPI_NOR_NORMAL:
|
||||
return 0;
|
||||
}
|
||||
@@ -961,6 +973,8 @@ static const struct flash_info spi_nor_i
|
||||
|
||||
/* ESMT */
|
||||
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
+ { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
+ { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
|
||||
/* Everspin */
|
||||
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
|
||||
@@ -1020,12 +1034,15 @@ static const struct flash_info spi_nor_i
|
||||
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
+ { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) },
|
||||
+ { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) },
|
||||
+ { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64, 0) },
|
||||
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
|
||||
- { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
|
||||
+ { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K) },
|
||||
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
|
||||
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
|
||||
@@ -1039,10 +1056,11 @@ static const struct flash_info spi_nor_i
|
||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
+ { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
- { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
- { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
+ { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
+ { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
|
||||
/* PMC */
|
||||
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
|
||||
@@ -1060,8 +1078,11 @@ static const struct flash_info spi_nor_i
|
||||
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
|
||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||
- { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
+ { "s25fs256s1", INFO6(0x010219, 0x4d0181, 64 * 1024, 512, 0)},
|
||||
+ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ
|
||||
+ | SPI_NOR_DDR_QUAD_READ) },
|
||||
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
+ { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)},
|
||||
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
|
||||
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
|
||||
@@ -1136,6 +1157,9 @@ static const struct flash_info spi_nor_i
|
||||
{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
+ { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
+ { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
+ { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{
|
||||
"w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64,
|
||||
@@ -1207,6 +1231,53 @@ static const struct flash_info *spi_nor_
|
||||
id[0], id[1], id[2]);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
+/*
|
||||
+ * The S25FS-S family physical sectors may be configured as a
|
||||
+ * hybrid combination of eight 4-kB parameter sectors
|
||||
+ * at the top or bottom of the address space with all
|
||||
+ * but one of the remaining sectors being uniform size.
|
||||
+ * The Parameter Sector Erase commands (20h or 21h) must
|
||||
+ * be used to erase the 4-kB parameter sectors individually.
|
||||
+ * The Sector (uniform sector) Erase commands (D8h or DCh)
|
||||
+ * must be used to erase any of the remaining
|
||||
+ * sectors, including the portion of highest or lowest address
|
||||
+ * sector that is not overlaid by the parameter sectors.
|
||||
+ * The uniform sector erase command has no effect on parameter sectors.
|
||||
+ */
|
||||
+static int spansion_s25fs_disable_4kb_erase(struct spi_nor *nor)
|
||||
+{
|
||||
+ struct fsl_qspi *q;
|
||||
+ u32 cr3v_addr = SPINOR_CONFIG_REG3_OFFSET;
|
||||
+ u8 cr3v = 0x0;
|
||||
+ int ret = 0x0;
|
||||
+
|
||||
+ q = nor->priv;
|
||||
+
|
||||
+ nor->cmd_buf[2] = cr3v_addr >> 16;
|
||||
+ nor->cmd_buf[1] = cr3v_addr >> 8;
|
||||
+ nor->cmd_buf[0] = cr3v_addr >> 0;
|
||||
+
|
||||
+ ret = nor->read_reg(nor, SPINOR_OP_SPANSION_RDAR, &cr3v, 1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (cr3v & CR3V_4KB_ERASE_UNABLE)
|
||||
+ return 0;
|
||||
+ ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ cr3v = CR3V_4KB_ERASE_UNABLE;
|
||||
+ nor->program_opcode = SPINOR_OP_SPANSION_WRAR;
|
||||
+ nor->write(nor, cr3v_addr, 1, &cr3v);
|
||||
+
|
||||
+ ret = nor->read_reg(nor, SPINOR_OP_SPANSION_RDAR, &cr3v, 1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (!(cr3v & CR3V_4KB_ERASE_UNABLE))
|
||||
+ return -EPERM;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
|
||||
static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
@@ -1426,7 +1497,7 @@ static int macronix_quad_enable(struct s
|
||||
* Write status Register and configuration register with 2 bytes
|
||||
* The first byte will be written to the status register, while the
|
||||
* second byte will be written to the configuration register.
|
||||
- * Return negative if error occured.
|
||||
+ * Return negative if error occurred.
|
||||
*/
|
||||
static int write_sr_cr(struct spi_nor *nor, u16 val)
|
||||
{
|
||||
@@ -1474,6 +1545,24 @@ static int spansion_quad_enable(struct s
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int set_ddr_quad_mode(struct spi_nor *nor, const struct flash_info *info)
|
||||
+{
|
||||
+ int status;
|
||||
+
|
||||
+ switch (JEDEC_MFR(info)) {
|
||||
+ case SNOR_MFR_SPANSION:
|
||||
+ status = spansion_quad_enable(nor);
|
||||
+ if (status) {
|
||||
+ dev_err(nor->dev, "Spansion DDR quad-read not enabled\n");
|
||||
+ return status;
|
||||
+ }
|
||||
+ return status;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
|
||||
{
|
||||
int status;
|
||||
@@ -1620,9 +1709,25 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
write_sr(nor, 0);
|
||||
spi_nor_wait_till_ready(nor);
|
||||
}
|
||||
+ if (JEDEC_MFR(info) == SNOR_MFR_MICRON) {
|
||||
+ ret = read_sr(nor);
|
||||
+ ret &= SPI_NOR_MICRON_WRITE_ENABLE;
|
||||
+
|
||||
+ write_enable(nor);
|
||||
+ write_sr(nor, ret);
|
||||
+ }
|
||||
+
|
||||
+ if (EXT_ID(info) == SPINOR_S25FS_FAMILY_ID) {
|
||||
+ ret = spansion_s25fs_disable_4kb_erase(nor);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
|
||||
if (!mtd->name)
|
||||
mtd->name = dev_name(dev);
|
||||
+ if (info->name)
|
||||
+ nor->vendor = info->name;
|
||||
mtd->priv = nor;
|
||||
mtd->type = MTD_NORFLASH;
|
||||
mtd->writesize = 1;
|
||||
@@ -1656,6 +1761,8 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
nor->flags |= SNOR_F_USE_FSR;
|
||||
if (info->flags & SPI_NOR_HAS_TB)
|
||||
nor->flags |= SNOR_F_HAS_SR_TB;
|
||||
+ if (info->flags & NO_CHIP_ERASE)
|
||||
+ nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
|
||||
|
||||
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
||||
/* prefer "small sector" erase if possible */
|
||||
@@ -1695,9 +1802,15 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
/* Some devices cannot do fast-read, no matter what DT tells us */
|
||||
if (info->flags & SPI_NOR_NO_FR)
|
||||
nor->flash_read = SPI_NOR_NORMAL;
|
||||
-
|
||||
- /* Quad/Dual-read mode takes precedence over fast/normal */
|
||||
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
|
||||
+ /* DDR Quad/Quad/Dual-read mode takes precedence over fast/normal */
|
||||
+ if (mode == SPI_NOR_DDR_QUAD && info->flags & SPI_NOR_DDR_QUAD_READ) {
|
||||
+ ret = set_ddr_quad_mode(nor, info);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "DDR quad mode not supported\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ nor->flash_read = SPI_NOR_DDR_QUAD;
|
||||
+ } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
|
||||
ret = set_quad_mode(nor, info);
|
||||
if (ret) {
|
||||
dev_err(dev, "quad mode not supported\n");
|
||||
@@ -1710,6 +1823,9 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
|
||||
/* Default commands */
|
||||
switch (nor->flash_read) {
|
||||
+ case SPI_NOR_DDR_QUAD:
|
||||
+ nor->read_opcode = SPINOR_OP_READ4_1_4_4_D;
|
||||
+ break;
|
||||
case SPI_NOR_QUAD:
|
||||
nor->read_opcode = SPINOR_OP_READ_1_1_4;
|
||||
break;
|
||||
--- a/include/linux/mtd/spi-nor.h
|
||||
+++ b/include/linux/mtd/spi-nor.h
|
||||
@@ -31,10 +31,10 @@
|
||||
|
||||
/*
|
||||
* Note on opcode nomenclature: some opcodes have a format like
|
||||
- * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
|
||||
+ * SPINOR_OP_FUNCTION{4,}_x_y_z{_D}. The numbers x, y,and z stand for the number
|
||||
* of I/O lines used for the opcode, address, and data (respectively). The
|
||||
* FUNCTION has an optional suffix of '4', to represent an opcode which
|
||||
- * requires a 4-byte (32-bit) address.
|
||||
+ * requires a 4-byte (32-bit) address. The suffix of 'D' stands for the
|
||||
*/
|
||||
|
||||
/* Flash opcodes. */
|
||||
@@ -46,7 +46,9 @@
|
||||
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
|
||||
#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
|
||||
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
|
||||
+#define SPINOR_OP_READ_1_4_4_D 0xed /* Read data bytes (DDR Quad SPI) */
|
||||
#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
|
||||
+#define SPINOR_OP_READ4_1_4_4_D 0xee /* Read data bytes (DDR Quad SPI) */
|
||||
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
|
||||
#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
|
||||
#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
|
||||
@@ -62,9 +64,11 @@
|
||||
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
|
||||
#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
|
||||
+#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
|
||||
#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
|
||||
#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
|
||||
#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
|
||||
+#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
|
||||
#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
|
||||
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
|
||||
#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
|
||||
@@ -94,6 +98,10 @@
|
||||
/* Used for Spansion flashes only. */
|
||||
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
|
||||
|
||||
+/* Used for Spansion S25FS-S family flash only. */
|
||||
+#define SPINOR_OP_SPANSION_RDAR 0x65 /* Read any device register */
|
||||
+#define SPINOR_OP_SPANSION_WRAR 0x71 /* Write any device register */
|
||||
+
|
||||
/* Used for Micron flashes only. */
|
||||
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
|
||||
#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
|
||||
@@ -124,6 +132,7 @@ enum read_mode {
|
||||
SPI_NOR_FAST,
|
||||
SPI_NOR_DUAL,
|
||||
SPI_NOR_QUAD,
|
||||
+ SPI_NOR_DDR_QUAD,
|
||||
};
|
||||
|
||||
#define SPI_NOR_MAX_CMD_SIZE 8
|
||||
@@ -189,6 +198,7 @@ struct spi_nor {
|
||||
bool sst_write_second;
|
||||
u32 flags;
|
||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
||||
+ char *vendor;
|
||||
|
||||
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
@@ -0,0 +1,392 @@
|
||||
From d9d0181f74146507026c31cccd52dda27ec3d966 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 14:57:31 +0800
|
||||
Subject: [PATCH 06/30] mtd: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape ifc-nor-nand support.
|
||||
|
||||
Signed-off-by: Alison Wang <b18965@freescale.com>
|
||||
Signed-off-by: Prabhakar Kushwaha <prabhakar.kushwaha@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/memory/Kconfig | 2 +-
|
||||
drivers/memory/fsl_ifc.c | 263 ++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/mtd/maps/physmap_of.c | 4 +
|
||||
drivers/mtd/nand/Kconfig | 2 +-
|
||||
drivers/mtd/nand/fsl_ifc_nand.c | 5 +-
|
||||
include/linux/fsl_ifc.h | 7 ++
|
||||
6 files changed, 280 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/memory/Kconfig
|
||||
+++ b/drivers/memory/Kconfig
|
||||
@@ -115,7 +115,7 @@ config FSL_CORENET_CF
|
||||
|
||||
config FSL_IFC
|
||||
bool
|
||||
- depends on FSL_SOC || ARCH_LAYERSCAPE
|
||||
+ depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
|
||||
config JZ4780_NEMC
|
||||
bool "Ingenic JZ4780 SoC NEMC driver"
|
||||
--- a/drivers/memory/fsl_ifc.c
|
||||
+++ b/drivers/memory/fsl_ifc.c
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
+#include <linux/delay.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
@@ -37,6 +38,8 @@
|
||||
|
||||
struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
|
||||
EXPORT_SYMBOL(fsl_ifc_ctrl_dev);
|
||||
+#define FSL_IFC_V1_3_0 0x01030000
|
||||
+#define IFC_TIMEOUT_MSECS 1000 /* 1000ms */
|
||||
|
||||
/*
|
||||
* convert_ifc_address - convert the base address
|
||||
@@ -311,6 +314,261 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_PM_SLEEP
|
||||
+/* save ifc registers */
|
||||
+static int fsl_ifc_suspend(struct device *dev)
|
||||
+{
|
||||
+ struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
+ struct fsl_ifc_global __iomem *fcm = ctrl->gregs;
|
||||
+ struct fsl_ifc_runtime __iomem *runtime = ctrl->rregs;
|
||||
+ __be32 nand_evter_intr_en, cm_evter_intr_en, nor_evter_intr_en,
|
||||
+ gpcm_evter_intr_en;
|
||||
+ uint32_t ifc_bank, i;
|
||||
+
|
||||
+ ctrl->saved_gregs = kzalloc(sizeof(struct fsl_ifc_global), GFP_KERNEL);
|
||||
+ if (!ctrl->saved_gregs)
|
||||
+ return -ENOMEM;
|
||||
+ ctrl->saved_rregs = kzalloc(sizeof(struct fsl_ifc_runtime), GFP_KERNEL);
|
||||
+ if (!ctrl->saved_rregs)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ cm_evter_intr_en = ifc_in32(&fcm->cm_evter_intr_en);
|
||||
+ nand_evter_intr_en = ifc_in32(&runtime->ifc_nand.nand_evter_intr_en);
|
||||
+ nor_evter_intr_en = ifc_in32(&runtime->ifc_nor.nor_evter_intr_en);
|
||||
+ gpcm_evter_intr_en = ifc_in32(&runtime->ifc_gpcm.gpcm_evter_intr_en);
|
||||
+
|
||||
+/* IFC interrupts disabled */
|
||||
+
|
||||
+ ifc_out32(0x0, &fcm->cm_evter_intr_en);
|
||||
+ ifc_out32(0x0, &runtime->ifc_nand.nand_evter_intr_en);
|
||||
+ ifc_out32(0x0, &runtime->ifc_nor.nor_evter_intr_en);
|
||||
+ ifc_out32(0x0, &runtime->ifc_gpcm.gpcm_evter_intr_en);
|
||||
+
|
||||
+ if (ctrl->saved_gregs) {
|
||||
+ for (ifc_bank = 0; ifc_bank < FSL_IFC_BANK_COUNT; ifc_bank++) {
|
||||
+ ctrl->saved_gregs->cspr_cs[ifc_bank].cspr_ext =
|
||||
+ ifc_in32(&fcm->cspr_cs[ifc_bank].cspr_ext);
|
||||
+ ctrl->saved_gregs->cspr_cs[ifc_bank].cspr =
|
||||
+ ifc_in32(&fcm->cspr_cs[ifc_bank].cspr);
|
||||
+ ctrl->saved_gregs->amask_cs[ifc_bank].amask =
|
||||
+ ifc_in32(&fcm->amask_cs[ifc_bank].amask);
|
||||
+ ctrl->saved_gregs->csor_cs[ifc_bank].csor_ext =
|
||||
+ ifc_in32(&fcm->csor_cs[ifc_bank].csor_ext);
|
||||
+ ctrl->saved_gregs->csor_cs[ifc_bank].csor =
|
||||
+ ifc_in32(&fcm->csor_cs[ifc_bank].csor);
|
||||
+ for (i = 0; i < 4; i++) {
|
||||
+ ctrl->saved_gregs->ftim_cs[ifc_bank].ftim[i] =
|
||||
+ ifc_in32(
|
||||
+ &fcm->ftim_cs[ifc_bank].ftim[i]);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ctrl->saved_gregs->rb_map = ifc_in32(&fcm->rb_map);
|
||||
+ ctrl->saved_gregs->wb_map = ifc_in32(&fcm->wb_map);
|
||||
+ ctrl->saved_gregs->ifc_gcr = ifc_in32(&fcm->ifc_gcr);
|
||||
+ ctrl->saved_gregs->ddr_ccr_low = ifc_in32(&fcm->ddr_ccr_low);
|
||||
+ ctrl->saved_gregs->cm_evter_en = ifc_in32(&fcm->cm_evter_en);
|
||||
+ }
|
||||
+
|
||||
+ if (ctrl->saved_rregs) {
|
||||
+ /* IFC controller NAND machine registers */
|
||||
+ ctrl->saved_rregs->ifc_nand.ncfgr =
|
||||
+ ifc_in32(&runtime->ifc_nand.ncfgr);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_fcr0 =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_fcr0);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_fcr1 =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_fcr1);
|
||||
+ ctrl->saved_rregs->ifc_nand.row0 =
|
||||
+ ifc_in32(&runtime->ifc_nand.row0);
|
||||
+ ctrl->saved_rregs->ifc_nand.row1 =
|
||||
+ ifc_in32(&runtime->ifc_nand.row1);
|
||||
+ ctrl->saved_rregs->ifc_nand.col0 =
|
||||
+ ifc_in32(&runtime->ifc_nand.col0);
|
||||
+ ctrl->saved_rregs->ifc_nand.col1 =
|
||||
+ ifc_in32(&runtime->ifc_nand.col1);
|
||||
+ ctrl->saved_rregs->ifc_nand.row2 =
|
||||
+ ifc_in32(&runtime->ifc_nand.row2);
|
||||
+ ctrl->saved_rregs->ifc_nand.col2 =
|
||||
+ ifc_in32(&runtime->ifc_nand.col2);
|
||||
+ ctrl->saved_rregs->ifc_nand.row3 =
|
||||
+ ifc_in32(&runtime->ifc_nand.row3);
|
||||
+ ctrl->saved_rregs->ifc_nand.col3 =
|
||||
+ ifc_in32(&runtime->ifc_nand.col3);
|
||||
+
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_fbcr =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_fbcr);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_fir0 =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_fir0);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_fir1 =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_fir1);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_fir2 =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_fir2);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_csel =
|
||||
+ ifc_in32(&runtime->ifc_nand.nand_csel);
|
||||
+ ctrl->saved_rregs->ifc_nand.nandseq_strt =
|
||||
+ ifc_in32(
|
||||
+ &runtime->ifc_nand.nandseq_strt);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_evter_en =
|
||||
+ ifc_in32(
|
||||
+ &runtime->ifc_nand.nand_evter_en);
|
||||
+ ctrl->saved_rregs->ifc_nand.nanndcr =
|
||||
+ ifc_in32(&runtime->ifc_nand.nanndcr);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_dll_lowcfg0 =
|
||||
+ ifc_in32(
|
||||
+ &runtime->ifc_nand.nand_dll_lowcfg0);
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_dll_lowcfg1 =
|
||||
+ ifc_in32(
|
||||
+ &runtime->ifc_nand.nand_dll_lowcfg1);
|
||||
+
|
||||
+ /* IFC controller NOR machine registers */
|
||||
+ ctrl->saved_rregs->ifc_nor.nor_evter_en =
|
||||
+ ifc_in32(
|
||||
+ &runtime->ifc_nor.nor_evter_en);
|
||||
+ ctrl->saved_rregs->ifc_nor.norcr =
|
||||
+ ifc_in32(&runtime->ifc_nor.norcr);
|
||||
+
|
||||
+ /* IFC controller GPCM Machine registers */
|
||||
+ ctrl->saved_rregs->ifc_gpcm.gpcm_evter_en =
|
||||
+ ifc_in32(
|
||||
+ &runtime->ifc_gpcm.gpcm_evter_en);
|
||||
+ }
|
||||
+
|
||||
+/* save the interrupt values */
|
||||
+ ctrl->saved_gregs->cm_evter_intr_en = cm_evter_intr_en;
|
||||
+ ctrl->saved_rregs->ifc_nand.nand_evter_intr_en = nand_evter_intr_en;
|
||||
+ ctrl->saved_rregs->ifc_nor.nor_evter_intr_en = nor_evter_intr_en;
|
||||
+ ctrl->saved_rregs->ifc_gpcm.gpcm_evter_intr_en = gpcm_evter_intr_en;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* restore ifc registers */
|
||||
+static int fsl_ifc_resume(struct device *dev)
|
||||
+{
|
||||
+ struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
+ struct fsl_ifc_global __iomem *fcm = ctrl->gregs;
|
||||
+ struct fsl_ifc_runtime __iomem *runtime = ctrl->rregs;
|
||||
+ struct fsl_ifc_global *savd_gregs = ctrl->saved_gregs;
|
||||
+ struct fsl_ifc_runtime *savd_rregs = ctrl->saved_rregs;
|
||||
+ uint32_t ver = 0, ncfgr, timeout, ifc_bank, i;
|
||||
+
|
||||
+/*
|
||||
+ * IFC interrupts disabled
|
||||
+ */
|
||||
+ ifc_out32(0x0, &fcm->cm_evter_intr_en);
|
||||
+ ifc_out32(0x0, &runtime->ifc_nand.nand_evter_intr_en);
|
||||
+ ifc_out32(0x0, &runtime->ifc_nor.nor_evter_intr_en);
|
||||
+ ifc_out32(0x0, &runtime->ifc_gpcm.gpcm_evter_intr_en);
|
||||
+
|
||||
+
|
||||
+ if (ctrl->saved_gregs) {
|
||||
+ for (ifc_bank = 0; ifc_bank < FSL_IFC_BANK_COUNT; ifc_bank++) {
|
||||
+ ifc_out32(savd_gregs->cspr_cs[ifc_bank].cspr_ext,
|
||||
+ &fcm->cspr_cs[ifc_bank].cspr_ext);
|
||||
+ ifc_out32(savd_gregs->cspr_cs[ifc_bank].cspr,
|
||||
+ &fcm->cspr_cs[ifc_bank].cspr);
|
||||
+ ifc_out32(savd_gregs->amask_cs[ifc_bank].amask,
|
||||
+ &fcm->amask_cs[ifc_bank].amask);
|
||||
+ ifc_out32(savd_gregs->csor_cs[ifc_bank].csor_ext,
|
||||
+ &fcm->csor_cs[ifc_bank].csor_ext);
|
||||
+ ifc_out32(savd_gregs->csor_cs[ifc_bank].csor,
|
||||
+ &fcm->csor_cs[ifc_bank].csor);
|
||||
+ for (i = 0; i < 4; i++) {
|
||||
+ ifc_out32(savd_gregs->ftim_cs[ifc_bank].ftim[i],
|
||||
+ &fcm->ftim_cs[ifc_bank].ftim[i]);
|
||||
+ }
|
||||
+ }
|
||||
+ ifc_out32(savd_gregs->rb_map, &fcm->rb_map);
|
||||
+ ifc_out32(savd_gregs->wb_map, &fcm->wb_map);
|
||||
+ ifc_out32(savd_gregs->ifc_gcr, &fcm->ifc_gcr);
|
||||
+ ifc_out32(savd_gregs->ddr_ccr_low, &fcm->ddr_ccr_low);
|
||||
+ ifc_out32(savd_gregs->cm_evter_en, &fcm->cm_evter_en);
|
||||
+ }
|
||||
+
|
||||
+ if (ctrl->saved_rregs) {
|
||||
+ /* IFC controller NAND machine registers */
|
||||
+ ifc_out32(savd_rregs->ifc_nand.ncfgr,
|
||||
+ &runtime->ifc_nand.ncfgr);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_fcr0,
|
||||
+ &runtime->ifc_nand.nand_fcr0);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_fcr1,
|
||||
+ &runtime->ifc_nand.nand_fcr1);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.row0, &runtime->ifc_nand.row0);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.row1, &runtime->ifc_nand.row1);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.col0, &runtime->ifc_nand.col0);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.col1, &runtime->ifc_nand.col1);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.row2, &runtime->ifc_nand.row2);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.col2, &runtime->ifc_nand.col2);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.row3, &runtime->ifc_nand.row3);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.col3, &runtime->ifc_nand.col3);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_fbcr,
|
||||
+ &runtime->ifc_nand.nand_fbcr);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_fir0,
|
||||
+ &runtime->ifc_nand.nand_fir0);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_fir1,
|
||||
+ &runtime->ifc_nand.nand_fir1);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_fir2,
|
||||
+ &runtime->ifc_nand.nand_fir2);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_csel,
|
||||
+ &runtime->ifc_nand.nand_csel);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nandseq_strt,
|
||||
+ &runtime->ifc_nand.nandseq_strt);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_evter_en,
|
||||
+ &runtime->ifc_nand.nand_evter_en);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nanndcr,
|
||||
+ &runtime->ifc_nand.nanndcr);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_dll_lowcfg0,
|
||||
+ &runtime->ifc_nand.nand_dll_lowcfg0);
|
||||
+ ifc_out32(savd_rregs->ifc_nand.nand_dll_lowcfg1,
|
||||
+ &runtime->ifc_nand.nand_dll_lowcfg1);
|
||||
+
|
||||
+ /* IFC controller NOR machine registers */
|
||||
+ ifc_out32(savd_rregs->ifc_nor.nor_evter_en,
|
||||
+ &runtime->ifc_nor.nor_evter_en);
|
||||
+ ifc_out32(savd_rregs->ifc_nor.norcr, &runtime->ifc_nor.norcr);
|
||||
+
|
||||
+ /* IFC controller GPCM Machine registers */
|
||||
+ ifc_out32(savd_rregs->ifc_gpcm.gpcm_evter_en,
|
||||
+ &runtime->ifc_gpcm.gpcm_evter_en);
|
||||
+
|
||||
+ /* IFC interrupts enabled */
|
||||
+ ifc_out32(ctrl->saved_gregs->cm_evter_intr_en,
|
||||
+ &fcm->cm_evter_intr_en);
|
||||
+ ifc_out32(ctrl->saved_rregs->ifc_nand.nand_evter_intr_en,
|
||||
+ &runtime->ifc_nand.nand_evter_intr_en);
|
||||
+ ifc_out32(ctrl->saved_rregs->ifc_nor.nor_evter_intr_en,
|
||||
+ &runtime->ifc_nor.nor_evter_intr_en);
|
||||
+ ifc_out32(ctrl->saved_rregs->ifc_gpcm.gpcm_evter_intr_en,
|
||||
+ &runtime->ifc_gpcm.gpcm_evter_intr_en);
|
||||
+
|
||||
+ kfree(ctrl->saved_gregs);
|
||||
+ kfree(ctrl->saved_rregs);
|
||||
+ ctrl->saved_gregs = NULL;
|
||||
+ ctrl->saved_rregs = NULL;
|
||||
+ }
|
||||
+
|
||||
+ ver = ifc_in32(&fcm->ifc_rev);
|
||||
+ ncfgr = ifc_in32(&runtime->ifc_nand.ncfgr);
|
||||
+ if (ver >= FSL_IFC_V1_3_0) {
|
||||
+
|
||||
+ ifc_out32(ncfgr | IFC_NAND_SRAM_INIT_EN,
|
||||
+ &runtime->ifc_nand.ncfgr);
|
||||
+ /* wait for SRAM_INIT bit to be clear or timeout */
|
||||
+ timeout = 10;
|
||||
+ while ((ifc_in32(&runtime->ifc_nand.ncfgr) &
|
||||
+ IFC_NAND_SRAM_INIT_EN) && timeout) {
|
||||
+ mdelay(IFC_TIMEOUT_MSECS);
|
||||
+ timeout--;
|
||||
+ }
|
||||
+
|
||||
+ if (!timeout)
|
||||
+ dev_err(ctrl->dev, "Timeout waiting for IFC SRAM INIT");
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif /* CONFIG_PM_SLEEP */
|
||||
+
|
||||
static const struct of_device_id fsl_ifc_match[] = {
|
||||
{
|
||||
.compatible = "fsl,ifc",
|
||||
@@ -318,10 +576,15 @@ static const struct of_device_id fsl_ifc
|
||||
{},
|
||||
};
|
||||
|
||||
+static const struct dev_pm_ops ifc_pm_ops = {
|
||||
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_ifc_suspend, fsl_ifc_resume)
|
||||
+};
|
||||
+
|
||||
static struct platform_driver fsl_ifc_ctrl_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-ifc",
|
||||
.of_match_table = fsl_ifc_match,
|
||||
+ .pm = &ifc_pm_ops,
|
||||
},
|
||||
.probe = fsl_ifc_ctrl_probe,
|
||||
.remove = fsl_ifc_ctrl_remove,
|
||||
--- a/drivers/mtd/maps/physmap_of.c
|
||||
+++ b/drivers/mtd/maps/physmap_of.c
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
+#include <linux/mtd/cfi_endian.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
@@ -201,6 +202,9 @@ static int of_flash_probe(struct platfor
|
||||
return err;
|
||||
}
|
||||
|
||||
+ if (of_property_read_bool(dp->parent, "big-endian"))
|
||||
+ info->list[i].map.swap = CFI_BIG_ENDIAN;
|
||||
+
|
||||
simple_map_init(&info->list[i].map);
|
||||
|
||||
/*
|
||||
--- a/drivers/mtd/nand/Kconfig
|
||||
+++ b/drivers/mtd/nand/Kconfig
|
||||
@@ -438,7 +438,7 @@ config MTD_NAND_FSL_ELBC
|
||||
|
||||
config MTD_NAND_FSL_IFC
|
||||
tristate "NAND support for Freescale IFC controller"
|
||||
- depends on FSL_SOC || ARCH_LAYERSCAPE
|
||||
+ depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
select FSL_IFC
|
||||
select MEMORY
|
||||
help
|
||||
--- a/drivers/mtd/nand/fsl_ifc_nand.c
|
||||
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
|
||||
@@ -901,7 +901,7 @@ static int fsl_ifc_chip_init(struct fsl_
|
||||
chip->ecc.algo = NAND_ECC_HAMMING;
|
||||
}
|
||||
|
||||
- if (ctrl->version == FSL_IFC_VERSION_1_1_0)
|
||||
+ if (ctrl->version >= FSL_IFC_VERSION_1_1_0)
|
||||
fsl_ifc_sram_init(priv);
|
||||
|
||||
/*
|
||||
--- a/include/linux/fsl_ifc.h
|
||||
+++ b/include/linux/fsl_ifc.h
|
||||
@@ -274,6 +274,8 @@
|
||||
*/
|
||||
/* Auto Boot Mode */
|
||||
#define IFC_NAND_NCFGR_BOOT 0x80000000
|
||||
+/* SRAM INIT EN */
|
||||
+#define IFC_NAND_SRAM_INIT_EN 0x20000000
|
||||
/* Addressing Mode-ROW0+n/COL0 */
|
||||
#define IFC_NAND_NCFGR_ADDR_MODE_RC0 0x00000000
|
||||
/* Addressing Mode-ROW0+n/COL0+n */
|
||||
@@ -857,6 +859,11 @@ struct fsl_ifc_ctrl {
|
||||
u32 nand_stat;
|
||||
wait_queue_head_t nand_wait;
|
||||
bool little_endian;
|
||||
+#ifdef CONFIG_PM_SLEEP
|
||||
+ /*save regs when system goes to deep sleep*/
|
||||
+ struct fsl_ifc_global *saved_gregs;
|
||||
+ struct fsl_ifc_runtime *saved_rregs;
|
||||
+#endif
|
||||
};
|
||||
|
||||
extern struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
|
||||
154537
target/linux/layerscape/patches-4.9/701-sdk_dpaa-support-layerscape.patch
Normal file
154537
target/linux/layerscape/patches-4.9/701-sdk_dpaa-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
2084
target/linux/layerscape/patches-4.9/702-pci-support-layerscape.patch
Normal file
2084
target/linux/layerscape/patches-4.9/702-pci-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
1763
target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch
Normal file
1763
target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
11297
target/linux/layerscape/patches-4.9/704-fsl-mc-layerscape-support.patch
Normal file
11297
target/linux/layerscape/patches-4.9/704-fsl-mc-layerscape-support.patch
Normal file
File diff suppressed because it is too large
Load Diff
23577
target/linux/layerscape/patches-4.9/705-dpaa2-support-layerscape.patch
Normal file
23577
target/linux/layerscape/patches-4.9/705-dpaa2-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
10616
target/linux/layerscape/patches-4.9/706-fsl_ppfe-support-layercape.patch
Normal file
10616
target/linux/layerscape/patches-4.9/706-fsl_ppfe-support-layercape.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,144 @@
|
||||
From 4c3979602db05bca439bfc98db88dc14a8663db0 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:14:57 +0800
|
||||
Subject: [PATCH 13/30] ata: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape sata support.
|
||||
|
||||
Signed-off-by: Tang Yuantian <Yuantian.Tang@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/ata/ahci_qoriq.c | 63 ++++++++++++++++++++++++++++++++++++++++++------
|
||||
1 file changed, 56 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/ata/ahci_qoriq.c
|
||||
+++ b/drivers/ata/ahci_qoriq.c
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Freescale QorIQ AHCI SATA platform driver
|
||||
*
|
||||
- * Copyright 2015 Freescale, Inc.
|
||||
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
|
||||
* Tang Yuantian <Yuantian.Tang@freescale.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -46,23 +46,32 @@
|
||||
#define LS1021A_AXICC_ADDR 0xC0
|
||||
|
||||
#define SATA_ECC_DISABLE 0x00020000
|
||||
+#define ECC_DIS_ARMV8_CH2 0x80000000
|
||||
+#define ECC_DIS_LS1088A 0x40000000
|
||||
|
||||
enum ahci_qoriq_type {
|
||||
AHCI_LS1021A,
|
||||
AHCI_LS1043A,
|
||||
AHCI_LS2080A,
|
||||
+ AHCI_LS1046A,
|
||||
+ AHCI_LS1088A,
|
||||
+ AHCI_LS2088A,
|
||||
};
|
||||
|
||||
struct ahci_qoriq_priv {
|
||||
struct ccsr_ahci *reg_base;
|
||||
enum ahci_qoriq_type type;
|
||||
void __iomem *ecc_addr;
|
||||
+ bool is_dmacoherent;
|
||||
};
|
||||
|
||||
static const struct of_device_id ahci_qoriq_of_match[] = {
|
||||
{ .compatible = "fsl,ls1021a-ahci", .data = (void *)AHCI_LS1021A},
|
||||
{ .compatible = "fsl,ls1043a-ahci", .data = (void *)AHCI_LS1043A},
|
||||
{ .compatible = "fsl,ls2080a-ahci", .data = (void *)AHCI_LS2080A},
|
||||
+ { .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A},
|
||||
+ { .compatible = "fsl,ls1088a-ahci", .data = (void *)AHCI_LS1088A},
|
||||
+ { .compatible = "fsl,ls2088a-ahci", .data = (void *)AHCI_LS2088A},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match);
|
||||
@@ -154,6 +163,8 @@ static int ahci_qoriq_phy_init(struct ah
|
||||
|
||||
switch (qpriv->type) {
|
||||
case AHCI_LS1021A:
|
||||
+ if (!qpriv->ecc_addr)
|
||||
+ return -EINVAL;
|
||||
writel(SATA_ECC_DISABLE, qpriv->ecc_addr);
|
||||
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||
writel(LS1021A_PORT_PHY2, reg_base + PORT_PHY2);
|
||||
@@ -161,19 +172,56 @@ static int ahci_qoriq_phy_init(struct ah
|
||||
writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4);
|
||||
writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5);
|
||||
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||
- writel(AHCI_PORT_AXICC_CFG, reg_base + LS1021A_AXICC_ADDR);
|
||||
+ if (qpriv->is_dmacoherent)
|
||||
+ writel(AHCI_PORT_AXICC_CFG,
|
||||
+ reg_base + LS1021A_AXICC_ADDR);
|
||||
break;
|
||||
|
||||
case AHCI_LS1043A:
|
||||
+ if (!qpriv->ecc_addr)
|
||||
+ return -EINVAL;
|
||||
+ writel(readl(qpriv->ecc_addr) | ECC_DIS_ARMV8_CH2,
|
||||
+ qpriv->ecc_addr);
|
||||
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||
- writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
+ if (qpriv->is_dmacoherent)
|
||||
+ writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
break;
|
||||
|
||||
case AHCI_LS2080A:
|
||||
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||
- writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
+ if (qpriv->is_dmacoherent)
|
||||
+ writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
+ break;
|
||||
+
|
||||
+ case AHCI_LS1046A:
|
||||
+ if (!qpriv->ecc_addr)
|
||||
+ return -EINVAL;
|
||||
+ writel(readl(qpriv->ecc_addr) | ECC_DIS_ARMV8_CH2,
|
||||
+ qpriv->ecc_addr);
|
||||
+ writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||
+ writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||
+ if (qpriv->is_dmacoherent)
|
||||
+ writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
+ break;
|
||||
+
|
||||
+ case AHCI_LS1088A:
|
||||
+ if (!qpriv->ecc_addr)
|
||||
+ return -EINVAL;
|
||||
+ writel(readl(qpriv->ecc_addr) | ECC_DIS_LS1088A,
|
||||
+ qpriv->ecc_addr);
|
||||
+ writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||
+ writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||
+ if (qpriv->is_dmacoherent)
|
||||
+ writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
+ break;
|
||||
+
|
||||
+ case AHCI_LS2088A:
|
||||
+ writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
|
||||
+ writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
|
||||
+ if (qpriv->is_dmacoherent)
|
||||
+ writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -204,13 +252,14 @@ static int ahci_qoriq_probe(struct platf
|
||||
|
||||
qoriq_priv->type = (enum ahci_qoriq_type)of_id->data;
|
||||
|
||||
- if (qoriq_priv->type == AHCI_LS1021A) {
|
||||
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
- "sata-ecc");
|
||||
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
+ "sata-ecc");
|
||||
+ if (res) {
|
||||
qoriq_priv->ecc_addr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(qoriq_priv->ecc_addr))
|
||||
return PTR_ERR(qoriq_priv->ecc_addr);
|
||||
}
|
||||
+ qoriq_priv->is_dmacoherent = of_dma_is_coherent(np);
|
||||
|
||||
rc = ahci_platform_enable_resources(hpriv);
|
||||
if (rc)
|
||||
@@ -0,0 +1,323 @@
|
||||
From 82a391a067491f4c46b75d0dfe2bf9e5a11aca8e Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:15:44 +0800
|
||||
Subject: [PATCH 14/30] clk: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape clock support.
|
||||
|
||||
Signed-off-by: Yuantian Tang <andy.tang@nxp.com>
|
||||
Signed-off-by: Mingkai Hu <mingkai.hu@nxp.com>
|
||||
Signed-off-by: Scott Wood <oss@buserror.net>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/clk/clk-qoriq.c | 179 ++++++++++++++++++++++++++++++++++++++++++++----
|
||||
1 file changed, 164 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk-qoriq.c
|
||||
+++ b/drivers/clk/clk-qoriq.c
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
+#include <linux/clkdev.h>
|
||||
#include <linux/fsl/guts.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -40,7 +41,7 @@ struct clockgen_pll_div {
|
||||
};
|
||||
|
||||
struct clockgen_pll {
|
||||
- struct clockgen_pll_div div[4];
|
||||
+ struct clockgen_pll_div div[8];
|
||||
};
|
||||
|
||||
#define CLKSEL_VALID 1
|
||||
@@ -87,7 +88,7 @@ struct clockgen {
|
||||
struct device_node *node;
|
||||
void __iomem *regs;
|
||||
struct clockgen_chipinfo info; /* mutable copy */
|
||||
- struct clk *sysclk;
|
||||
+ struct clk *sysclk, *coreclk;
|
||||
struct clockgen_pll pll[6];
|
||||
struct clk *cmux[NUM_CMUX];
|
||||
struct clk *hwaccel[NUM_HWACCEL];
|
||||
@@ -266,6 +267,39 @@ static const struct clockgen_muxinfo ls1
|
||||
},
|
||||
};
|
||||
|
||||
+static const struct clockgen_muxinfo ls1046a_hwa1 = {
|
||||
+ {
|
||||
+ {},
|
||||
+ {},
|
||||
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
|
||||
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
|
||||
+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
|
||||
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
|
||||
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct clockgen_muxinfo ls1046a_hwa2 = {
|
||||
+ {
|
||||
+ {},
|
||||
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
|
||||
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
|
||||
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
|
||||
+ {},
|
||||
+ {},
|
||||
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct clockgen_muxinfo ls1012a_cmux = {
|
||||
+ {
|
||||
+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
|
||||
+ {},
|
||||
+ [2] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
static const struct clockgen_muxinfo t1023_hwa1 = {
|
||||
{
|
||||
{},
|
||||
@@ -489,6 +523,42 @@ static const struct clockgen_chipinfo ch
|
||||
.flags = CG_PLL_8BIT,
|
||||
},
|
||||
{
|
||||
+ .compat = "fsl,ls1046a-clockgen",
|
||||
+ .init_periph = t2080_init_periph,
|
||||
+ .cmux_groups = {
|
||||
+ &t1040_cmux
|
||||
+ },
|
||||
+ .hwaccel = {
|
||||
+ &ls1046a_hwa1, &ls1046a_hwa2
|
||||
+ },
|
||||
+ .cmux_to_group = {
|
||||
+ 0, -1
|
||||
+ },
|
||||
+ .pll_mask = 0x07,
|
||||
+ .flags = CG_PLL_8BIT,
|
||||
+ },
|
||||
+ {
|
||||
+ .compat = "fsl,ls1088a-clockgen",
|
||||
+ .cmux_groups = {
|
||||
+ &clockgen2_cmux_cga12
|
||||
+ },
|
||||
+ .cmux_to_group = {
|
||||
+ 0, 0, -1
|
||||
+ },
|
||||
+ .pll_mask = 0x07,
|
||||
+ .flags = CG_VER3 | CG_LITTLE_ENDIAN,
|
||||
+ },
|
||||
+ {
|
||||
+ .compat = "fsl,ls1012a-clockgen",
|
||||
+ .cmux_groups = {
|
||||
+ &ls1012a_cmux
|
||||
+ },
|
||||
+ .cmux_to_group = {
|
||||
+ 0, -1
|
||||
+ },
|
||||
+ .pll_mask = 0x03,
|
||||
+ },
|
||||
+ {
|
||||
.compat = "fsl,ls2080a-clockgen",
|
||||
.cmux_groups = {
|
||||
&clockgen2_cmux_cga12, &clockgen2_cmux_cgb
|
||||
@@ -846,7 +916,12 @@ static void __init create_muxes(struct c
|
||||
|
||||
static void __init clockgen_init(struct device_node *np);
|
||||
|
||||
-/* Legacy nodes may get probed before the parent clockgen node */
|
||||
+/*
|
||||
+ * Legacy nodes may get probed before the parent clockgen node.
|
||||
+ * It is assumed that device trees with legacy nodes will not
|
||||
+ * contain a "clocks" property -- otherwise the input clocks may
|
||||
+ * not be initialized at this point.
|
||||
+ */
|
||||
static void __init legacy_init_clockgen(struct device_node *np)
|
||||
{
|
||||
if (!clockgen.node)
|
||||
@@ -887,18 +962,13 @@ static struct clk __init
|
||||
return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
}
|
||||
|
||||
-static struct clk *sysclk_from_parent(const char *name)
|
||||
+static struct clk __init *input_clock(const char *name, struct clk *clk)
|
||||
{
|
||||
- struct clk *clk;
|
||||
- const char *parent_name;
|
||||
-
|
||||
- clk = of_clk_get(clockgen.node, 0);
|
||||
- if (IS_ERR(clk))
|
||||
- return clk;
|
||||
+ const char *input_name;
|
||||
|
||||
/* Register the input clock under the desired name. */
|
||||
- parent_name = __clk_get_name(clk);
|
||||
- clk = clk_register_fixed_factor(NULL, name, parent_name,
|
||||
+ input_name = __clk_get_name(clk);
|
||||
+ clk = clk_register_fixed_factor(NULL, name, input_name,
|
||||
0, 1, 1);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: Couldn't register %s: %ld\n", __func__, name,
|
||||
@@ -907,6 +977,29 @@ static struct clk *sysclk_from_parent(co
|
||||
return clk;
|
||||
}
|
||||
|
||||
+static struct clk __init *input_clock_by_name(const char *name,
|
||||
+ const char *dtname)
|
||||
+{
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ clk = of_clk_get_by_name(clockgen.node, dtname);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return clk;
|
||||
+
|
||||
+ return input_clock(name, clk);
|
||||
+}
|
||||
+
|
||||
+static struct clk __init *input_clock_by_index(const char *name, int idx)
|
||||
+{
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ clk = of_clk_get(clockgen.node, 0);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return clk;
|
||||
+
|
||||
+ return input_clock(name, clk);
|
||||
+}
|
||||
+
|
||||
static struct clk * __init create_sysclk(const char *name)
|
||||
{
|
||||
struct device_node *sysclk;
|
||||
@@ -916,7 +1009,11 @@ static struct clk * __init create_sysclk
|
||||
if (!IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
- clk = sysclk_from_parent(name);
|
||||
+ clk = input_clock_by_name(name, "sysclk");
|
||||
+ if (!IS_ERR(clk))
|
||||
+ return clk;
|
||||
+
|
||||
+ clk = input_clock_by_index(name, 0);
|
||||
if (!IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
@@ -927,7 +1024,27 @@ static struct clk * __init create_sysclk
|
||||
return clk;
|
||||
}
|
||||
|
||||
- pr_err("%s: No input clock\n", __func__);
|
||||
+ pr_err("%s: No input sysclk\n", __func__);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static struct clk * __init create_coreclk(const char *name)
|
||||
+{
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ clk = input_clock_by_name(name, "coreclk");
|
||||
+ if (!IS_ERR(clk))
|
||||
+ return clk;
|
||||
+
|
||||
+ /*
|
||||
+ * This indicates a mix of legacy nodes with the new coreclk
|
||||
+ * mechanism, which should never happen. If this error occurs,
|
||||
+ * don't use the wrong input clock just because coreclk isn't
|
||||
+ * ready yet.
|
||||
+ */
|
||||
+ if (WARN_ON(PTR_ERR(clk) == -EPROBE_DEFER))
|
||||
+ return clk;
|
||||
+
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -950,11 +1067,19 @@ static void __init create_one_pll(struct
|
||||
u32 __iomem *reg;
|
||||
u32 mult;
|
||||
struct clockgen_pll *pll = &cg->pll[idx];
|
||||
+ const char *input = "cg-sysclk";
|
||||
int i;
|
||||
|
||||
if (!(cg->info.pll_mask & (1 << idx)))
|
||||
return;
|
||||
|
||||
+ if (cg->coreclk && idx != PLATFORM_PLL) {
|
||||
+ if (IS_ERR(cg->coreclk))
|
||||
+ return;
|
||||
+
|
||||
+ input = "cg-coreclk";
|
||||
+ }
|
||||
+
|
||||
if (cg->info.flags & CG_VER3) {
|
||||
switch (idx) {
|
||||
case PLATFORM_PLL:
|
||||
@@ -1000,12 +1125,20 @@ static void __init create_one_pll(struct
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pll->div); i++) {
|
||||
struct clk *clk;
|
||||
+ int ret;
|
||||
+
|
||||
+ /*
|
||||
+ * For platform PLL, there are 8 divider clocks.
|
||||
+ * For core PLL, there are 4 divider clocks at most.
|
||||
+ */
|
||||
+ if (idx != 0 && i >= 4)
|
||||
+ break;
|
||||
|
||||
snprintf(pll->div[i].name, sizeof(pll->div[i].name),
|
||||
"cg-pll%d-div%d", idx, i + 1);
|
||||
|
||||
clk = clk_register_fixed_factor(NULL,
|
||||
- pll->div[i].name, "cg-sysclk", 0, mult, i + 1);
|
||||
+ pll->div[i].name, input, 0, mult, i + 1);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: %s: register failed %ld\n",
|
||||
__func__, pll->div[i].name, PTR_ERR(clk));
|
||||
@@ -1013,6 +1146,11 @@ static void __init create_one_pll(struct
|
||||
}
|
||||
|
||||
pll->div[i].clk = clk;
|
||||
+ ret = clk_register_clkdev(clk, pll->div[i].name, NULL);
|
||||
+ if (ret != 0)
|
||||
+ pr_err("%s: %s: register to lookup table failed %ld\n",
|
||||
+ __func__, pll->div[i].name, PTR_ERR(clk));
|
||||
+
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1142,6 +1280,13 @@ static struct clk *clockgen_clk_get(stru
|
||||
goto bad_args;
|
||||
clk = pll->div[idx].clk;
|
||||
break;
|
||||
+ case 5:
|
||||
+ if (idx != 0)
|
||||
+ goto bad_args;
|
||||
+ clk = cg->coreclk;
|
||||
+ if (IS_ERR(clk))
|
||||
+ clk = NULL;
|
||||
+ break;
|
||||
default:
|
||||
goto bad_args;
|
||||
}
|
||||
@@ -1254,6 +1399,7 @@ static void __init clockgen_init(struct
|
||||
clockgen.info.flags |= CG_CMUX_GE_PLAT;
|
||||
|
||||
clockgen.sysclk = create_sysclk("cg-sysclk");
|
||||
+ clockgen.coreclk = create_coreclk("cg-coreclk");
|
||||
create_plls(&clockgen);
|
||||
create_muxes(&clockgen);
|
||||
|
||||
@@ -1274,8 +1420,11 @@ err:
|
||||
|
||||
CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init);
|
||||
+CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init);
|
||||
+CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init);
|
||||
+CLK_OF_DECLARE(qoriq_clockgen_ls1088a, "fsl,ls1088a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init);
|
||||
|
||||
/* Legacy nodes */
|
||||
@@ -0,0 +1,524 @@
|
||||
From b018e44a68dc2f4df819ae194e39e07313841dad Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:27:58 +0800
|
||||
Subject: [PATCH 15/30] cpufreq: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape pm support.
|
||||
|
||||
Signed-off-by: Tang Yuantian <Yuantian.Tang@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/cpufreq/Kconfig | 2 +-
|
||||
drivers/cpufreq/qoriq-cpufreq.c | 176 +++++++++++++++-------------------------
|
||||
drivers/firmware/psci.c | 12 ++-
|
||||
drivers/soc/fsl/rcpm.c | 158 ++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 235 insertions(+), 113 deletions(-)
|
||||
create mode 100644 drivers/soc/fsl/rcpm.c
|
||||
|
||||
--- a/drivers/cpufreq/Kconfig
|
||||
+++ b/drivers/cpufreq/Kconfig
|
||||
@@ -334,7 +334,7 @@ endif
|
||||
|
||||
config QORIQ_CPUFREQ
|
||||
tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
|
||||
- depends on OF && COMMON_CLK && (PPC_E500MC || ARM)
|
||||
+ depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64)
|
||||
depends on !CPU_THERMAL || THERMAL
|
||||
select CLK_QORIQ
|
||||
help
|
||||
--- a/drivers/cpufreq/qoriq-cpufreq.c
|
||||
+++ b/drivers/cpufreq/qoriq-cpufreq.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/errno.h>
|
||||
@@ -22,10 +23,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
-#if !defined(CONFIG_ARM)
|
||||
-#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */
|
||||
-#endif
|
||||
-
|
||||
/**
|
||||
* struct cpu_data
|
||||
* @pclk: the parent clock of cpu
|
||||
@@ -37,73 +34,51 @@ struct cpu_data {
|
||||
struct thermal_cooling_device *cdev;
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * Don't use cpufreq on this SoC -- used when the SoC would have otherwise
|
||||
+ * matched a more generic compatible.
|
||||
+ */
|
||||
+#define SOC_BLACKLIST 1
|
||||
+
|
||||
/**
|
||||
* struct soc_data - SoC specific data
|
||||
- * @freq_mask: mask the disallowed frequencies
|
||||
- * @flag: unique flags
|
||||
+ * @flags: SOC_xxx
|
||||
*/
|
||||
struct soc_data {
|
||||
- u32 freq_mask[4];
|
||||
- u32 flag;
|
||||
-};
|
||||
-
|
||||
-#define FREQ_MASK 1
|
||||
-/* see hardware specification for the allowed frqeuencies */
|
||||
-static const struct soc_data sdata[] = {
|
||||
- { /* used by p2041 and p3041 */
|
||||
- .freq_mask = {0x8, 0x8, 0x2, 0x2},
|
||||
- .flag = FREQ_MASK,
|
||||
- },
|
||||
- { /* used by p5020 */
|
||||
- .freq_mask = {0x8, 0x2},
|
||||
- .flag = FREQ_MASK,
|
||||
- },
|
||||
- { /* used by p4080, p5040 */
|
||||
- .freq_mask = {0},
|
||||
- .flag = 0,
|
||||
- },
|
||||
+ u32 flags;
|
||||
};
|
||||
|
||||
-/*
|
||||
- * the minimum allowed core frequency, in Hz
|
||||
- * for chassis v1.0, >= platform frequency
|
||||
- * for chassis v2.0, >= platform frequency / 2
|
||||
- */
|
||||
-static u32 min_cpufreq;
|
||||
-static const u32 *fmask;
|
||||
-
|
||||
-#if defined(CONFIG_ARM)
|
||||
-static int get_cpu_physical_id(int cpu)
|
||||
-{
|
||||
- return topology_core_id(cpu);
|
||||
-}
|
||||
-#else
|
||||
-static int get_cpu_physical_id(int cpu)
|
||||
-{
|
||||
- return get_hard_smp_processor_id(cpu);
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
static u32 get_bus_freq(void)
|
||||
{
|
||||
struct device_node *soc;
|
||||
u32 sysfreq;
|
||||
+ struct clk *pltclk;
|
||||
+ int ret;
|
||||
|
||||
+ /* get platform freq by searching bus-frequency property */
|
||||
soc = of_find_node_by_type(NULL, "soc");
|
||||
- if (!soc)
|
||||
- return 0;
|
||||
-
|
||||
- if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
|
||||
- sysfreq = 0;
|
||||
+ if (soc) {
|
||||
+ ret = of_property_read_u32(soc, "bus-frequency", &sysfreq);
|
||||
+ of_node_put(soc);
|
||||
+ if (!ret)
|
||||
+ return sysfreq;
|
||||
+ }
|
||||
|
||||
- of_node_put(soc);
|
||||
+ /* get platform freq by its clock name */
|
||||
+ pltclk = clk_get(NULL, "cg-pll0-div1");
|
||||
+ if (IS_ERR(pltclk)) {
|
||||
+ pr_err("%s: can't get bus frequency %ld\n",
|
||||
+ __func__, PTR_ERR(pltclk));
|
||||
+ return PTR_ERR(pltclk);
|
||||
+ }
|
||||
|
||||
- return sysfreq;
|
||||
+ return clk_get_rate(pltclk);
|
||||
}
|
||||
|
||||
-static struct device_node *cpu_to_clk_node(int cpu)
|
||||
+static struct clk *cpu_to_clk(int cpu)
|
||||
{
|
||||
- struct device_node *np, *clk_np;
|
||||
+ struct device_node *np;
|
||||
+ struct clk *clk;
|
||||
|
||||
if (!cpu_present(cpu))
|
||||
return NULL;
|
||||
@@ -112,37 +87,28 @@ static struct device_node *cpu_to_clk_no
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
- clk_np = of_parse_phandle(np, "clocks", 0);
|
||||
- if (!clk_np)
|
||||
- return NULL;
|
||||
-
|
||||
+ clk = of_clk_get(np, 0);
|
||||
of_node_put(np);
|
||||
-
|
||||
- return clk_np;
|
||||
+ return clk;
|
||||
}
|
||||
|
||||
/* traverse cpu nodes to get cpu mask of sharing clock wire */
|
||||
static void set_affected_cpus(struct cpufreq_policy *policy)
|
||||
{
|
||||
- struct device_node *np, *clk_np;
|
||||
struct cpumask *dstp = policy->cpus;
|
||||
+ struct clk *clk;
|
||||
int i;
|
||||
|
||||
- np = cpu_to_clk_node(policy->cpu);
|
||||
- if (!np)
|
||||
- return;
|
||||
-
|
||||
for_each_present_cpu(i) {
|
||||
- clk_np = cpu_to_clk_node(i);
|
||||
- if (!clk_np)
|
||||
+ clk = cpu_to_clk(i);
|
||||
+ if (IS_ERR(clk)) {
|
||||
+ pr_err("%s: no clock for cpu %d\n", __func__, i);
|
||||
continue;
|
||||
+ }
|
||||
|
||||
- if (clk_np == np)
|
||||
+ if (clk_is_match(policy->clk, clk))
|
||||
cpumask_set_cpu(i, dstp);
|
||||
-
|
||||
- of_node_put(clk_np);
|
||||
}
|
||||
- of_node_put(np);
|
||||
}
|
||||
|
||||
/* reduce the duplicated frequencies in frequency table */
|
||||
@@ -198,10 +164,11 @@ static void freq_table_sort(struct cpufr
|
||||
|
||||
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
- struct device_node *np, *pnode;
|
||||
+ struct device_node *np;
|
||||
int i, count, ret;
|
||||
- u32 freq, mask;
|
||||
+ u32 freq;
|
||||
struct clk *clk;
|
||||
+ const struct clk_hw *hwclk;
|
||||
struct cpufreq_frequency_table *table;
|
||||
struct cpu_data *data;
|
||||
unsigned int cpu = policy->cpu;
|
||||
@@ -221,17 +188,13 @@ static int qoriq_cpufreq_cpu_init(struct
|
||||
goto err_nomem2;
|
||||
}
|
||||
|
||||
- pnode = of_parse_phandle(np, "clocks", 0);
|
||||
- if (!pnode) {
|
||||
- pr_err("%s: could not get clock information\n", __func__);
|
||||
- goto err_nomem2;
|
||||
- }
|
||||
+ hwclk = __clk_get_hw(policy->clk);
|
||||
+ count = clk_hw_get_num_parents(hwclk);
|
||||
|
||||
- count = of_property_count_strings(pnode, "clock-names");
|
||||
data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!data->pclk) {
|
||||
pr_err("%s: no memory\n", __func__);
|
||||
- goto err_node;
|
||||
+ goto err_nomem2;
|
||||
}
|
||||
|
||||
table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
|
||||
@@ -240,23 +203,11 @@ static int qoriq_cpufreq_cpu_init(struct
|
||||
goto err_pclk;
|
||||
}
|
||||
|
||||
- if (fmask)
|
||||
- mask = fmask[get_cpu_physical_id(cpu)];
|
||||
- else
|
||||
- mask = 0x0;
|
||||
-
|
||||
for (i = 0; i < count; i++) {
|
||||
- clk = of_clk_get(pnode, i);
|
||||
+ clk = clk_hw_get_parent_by_index(hwclk, i)->clk;
|
||||
data->pclk[i] = clk;
|
||||
freq = clk_get_rate(clk);
|
||||
- /*
|
||||
- * the clock is valid if its frequency is not masked
|
||||
- * and large than minimum allowed frequency.
|
||||
- */
|
||||
- if (freq < min_cpufreq || (mask & (1 << i)))
|
||||
- table[i].frequency = CPUFREQ_ENTRY_INVALID;
|
||||
- else
|
||||
- table[i].frequency = freq / 1000;
|
||||
+ table[i].frequency = freq / 1000;
|
||||
table[i].driver_data = i;
|
||||
}
|
||||
freq_table_redup(table, count);
|
||||
@@ -282,7 +233,6 @@ static int qoriq_cpufreq_cpu_init(struct
|
||||
policy->cpuinfo.transition_latency = u64temp + 1;
|
||||
|
||||
of_node_put(np);
|
||||
- of_node_put(pnode);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -290,10 +240,7 @@ err_nomem1:
|
||||
kfree(table);
|
||||
err_pclk:
|
||||
kfree(data->pclk);
|
||||
-err_node:
|
||||
- of_node_put(pnode);
|
||||
err_nomem2:
|
||||
- policy->driver_data = NULL;
|
||||
kfree(data);
|
||||
err_np:
|
||||
of_node_put(np);
|
||||
@@ -357,12 +304,25 @@ static struct cpufreq_driver qoriq_cpufr
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
+static const struct soc_data blacklist = {
|
||||
+ .flags = SOC_BLACKLIST,
|
||||
+};
|
||||
+
|
||||
static const struct of_device_id node_matches[] __initconst = {
|
||||
- { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
|
||||
- { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
|
||||
- { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
|
||||
- { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
|
||||
- { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
|
||||
+ /* e6500 cannot use cpufreq due to erratum A-008083 */
|
||||
+ { .compatible = "fsl,b4420-clockgen", &blacklist },
|
||||
+ { .compatible = "fsl,b4860-clockgen", &blacklist },
|
||||
+ { .compatible = "fsl,t2080-clockgen", &blacklist },
|
||||
+ { .compatible = "fsl,t4240-clockgen", &blacklist },
|
||||
+
|
||||
+ { .compatible = "fsl,ls1012a-clockgen", },
|
||||
+ { .compatible = "fsl,ls1021a-clockgen", },
|
||||
+ { .compatible = "fsl,ls1043a-clockgen", },
|
||||
+ { .compatible = "fsl,ls1046a-clockgen", },
|
||||
+ { .compatible = "fsl,ls1088a-clockgen", },
|
||||
+ { .compatible = "fsl,ls2080a-clockgen", },
|
||||
+ { .compatible = "fsl,p4080-clockgen", },
|
||||
+ { .compatible = "fsl,qoriq-clockgen-1.0", },
|
||||
{ .compatible = "fsl,qoriq-clockgen-2.0", },
|
||||
{}
|
||||
};
|
||||
@@ -380,16 +340,12 @@ static int __init qoriq_cpufreq_init(voi
|
||||
|
||||
match = of_match_node(node_matches, np);
|
||||
data = match->data;
|
||||
- if (data) {
|
||||
- if (data->flag)
|
||||
- fmask = data->freq_mask;
|
||||
- min_cpufreq = get_bus_freq();
|
||||
- } else {
|
||||
- min_cpufreq = get_bus_freq() / 2;
|
||||
- }
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
+ if (data && data->flags & SOC_BLACKLIST)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
|
||||
if (!ret)
|
||||
pr_info("Freescale QorIQ CPU frequency scaling driver\n");
|
||||
--- a/drivers/firmware/psci.c
|
||||
+++ b/drivers/firmware/psci.c
|
||||
@@ -437,8 +437,12 @@ CPUIDLE_METHOD_OF_DECLARE(psci, "psci",
|
||||
|
||||
static int psci_system_suspend(unsigned long unused)
|
||||
{
|
||||
- return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
|
||||
- virt_to_phys(cpu_resume), 0, 0);
|
||||
+ u32 state;
|
||||
+
|
||||
+ state = ( 2 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) |
|
||||
+ (1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT);
|
||||
+
|
||||
+ return psci_cpu_suspend(state, virt_to_phys(cpu_resume));
|
||||
}
|
||||
|
||||
static int psci_system_suspend_enter(suspend_state_t state)
|
||||
@@ -458,6 +462,8 @@ static void __init psci_init_system_susp
|
||||
if (!IS_ENABLED(CONFIG_SUSPEND))
|
||||
return;
|
||||
|
||||
+ suspend_set_ops(&psci_suspend_ops);
|
||||
+
|
||||
ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
|
||||
|
||||
if (ret != PSCI_RET_NOT_SUPPORTED)
|
||||
@@ -562,6 +568,8 @@ static void __init psci_0_2_set_function
|
||||
arm_pm_restart = psci_sys_reset;
|
||||
|
||||
pm_power_off = psci_sys_poweroff;
|
||||
+ psci_init_system_suspend();
|
||||
+ suspend_set_ops(&psci_suspend_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/rcpm.c
|
||||
@@ -0,0 +1,158 @@
|
||||
+/*
|
||||
+ * Run Control and Power Management (RCPM) driver
|
||||
+ *
|
||||
+ * Copyright 2016 NXP
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ */
|
||||
+#define pr_fmt(fmt) "RCPM: %s: " fmt, __func__
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/suspend.h>
|
||||
+
|
||||
+/* RCPM register offset */
|
||||
+#define RCPM_IPPDEXPCR0 0x140
|
||||
+
|
||||
+#define RCPM_WAKEUP_CELL_SIZE 2
|
||||
+
|
||||
+struct rcpm_config {
|
||||
+ int ipp_num;
|
||||
+ int ippdexpcr_offset;
|
||||
+ u32 ippdexpcr[2];
|
||||
+ void *rcpm_reg_base;
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_config *rcpm;
|
||||
+
|
||||
+static inline void rcpm_reg_write(u32 offset, u32 value)
|
||||
+{
|
||||
+ iowrite32be(value, rcpm->rcpm_reg_base + offset);
|
||||
+}
|
||||
+
|
||||
+static inline u32 rcpm_reg_read(u32 offset)
|
||||
+{
|
||||
+ return ioread32be(rcpm->rcpm_reg_base + offset);
|
||||
+}
|
||||
+
|
||||
+static void rcpm_wakeup_fixup(struct device *dev, void *data)
|
||||
+{
|
||||
+ struct device_node *node = dev ? dev->of_node : NULL;
|
||||
+ u32 value[RCPM_WAKEUP_CELL_SIZE];
|
||||
+ int ret, i;
|
||||
+
|
||||
+ if (!dev || !node || !device_may_wakeup(dev))
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * Get the values in the "rcpm-wakeup" property.
|
||||
+ * Three values are:
|
||||
+ * The first is a pointer to the RCPM node.
|
||||
+ * The second is the value of the ippdexpcr0 register.
|
||||
+ * The third is the value of the ippdexpcr1 register.
|
||||
+ */
|
||||
+ ret = of_property_read_u32_array(node, "fsl,rcpm-wakeup",
|
||||
+ value, RCPM_WAKEUP_CELL_SIZE);
|
||||
+ if (ret)
|
||||
+ return;
|
||||
+
|
||||
+ pr_debug("wakeup source: the device %s\n", node->full_name);
|
||||
+
|
||||
+ for (i = 0; i < rcpm->ipp_num; i++)
|
||||
+ rcpm->ippdexpcr[i] |= value[i + 1];
|
||||
+}
|
||||
+
|
||||
+static int rcpm_suspend_prepare(void)
|
||||
+{
|
||||
+ int i;
|
||||
+ u32 val;
|
||||
+
|
||||
+ BUG_ON(!rcpm);
|
||||
+
|
||||
+ for (i = 0; i < rcpm->ipp_num; i++)
|
||||
+ rcpm->ippdexpcr[i] = 0;
|
||||
+
|
||||
+ dpm_for_each_dev(NULL, rcpm_wakeup_fixup);
|
||||
+
|
||||
+ for (i = 0; i < rcpm->ipp_num; i++) {
|
||||
+ if (rcpm->ippdexpcr[i]) {
|
||||
+ val = rcpm_reg_read(rcpm->ippdexpcr_offset + 4 * i);
|
||||
+ rcpm_reg_write(rcpm->ippdexpcr_offset + 4 * i,
|
||||
+ val | rcpm->ippdexpcr[i]);
|
||||
+ pr_debug("ippdexpcr%d = 0x%x\n", i, rcpm->ippdexpcr[i]);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rcpm_suspend_notifier_call(struct notifier_block *bl,
|
||||
+ unsigned long state,
|
||||
+ void *unused)
|
||||
+{
|
||||
+ switch (state) {
|
||||
+ case PM_SUSPEND_PREPARE:
|
||||
+ rcpm_suspend_prepare();
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return NOTIFY_DONE;
|
||||
+}
|
||||
+
|
||||
+static struct rcpm_config rcpm_default_config = {
|
||||
+ .ipp_num = 1,
|
||||
+ .ippdexpcr_offset = RCPM_IPPDEXPCR0,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id rcpm_matches[] = {
|
||||
+ {
|
||||
+ .compatible = "fsl,qoriq-rcpm-2.1",
|
||||
+ .data = &rcpm_default_config,
|
||||
+ },
|
||||
+ {}
|
||||
+};
|
||||
+
|
||||
+static struct notifier_block rcpm_suspend_notifier = {
|
||||
+ .notifier_call = rcpm_suspend_notifier_call,
|
||||
+};
|
||||
+
|
||||
+static int __init layerscape_rcpm_init(void)
|
||||
+{
|
||||
+ const struct of_device_id *match;
|
||||
+ struct device_node *np;
|
||||
+
|
||||
+ np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
|
||||
+ if (!np) {
|
||||
+ pr_err("Can't find the RCPM node.\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (match->data)
|
||||
+ rcpm = (struct rcpm_config *)match->data;
|
||||
+ else
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ rcpm->rcpm_reg_base = of_iomap(np, 0);
|
||||
+ of_node_put(np);
|
||||
+ if (!rcpm->rcpm_reg_base)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ register_pm_notifier(&rcpm_suspend_notifier);
|
||||
+
|
||||
+ pr_info("The RCPM driver initialized.\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+subsys_initcall(layerscape_rcpm_init);
|
||||
28784
target/linux/layerscape/patches-4.9/804-crypto-support-layerscape.patch
Normal file
28784
target/linux/layerscape/patches-4.9/804-crypto-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
4392
target/linux/layerscape/patches-4.9/805-dma-support-layerscape.patch
Normal file
4392
target/linux/layerscape/patches-4.9/805-dma-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,404 @@
|
||||
From b92e223750a07b28f175eae97d5ce3978df41be8 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:32:05 +0800
|
||||
Subject: [PATCH 18/30] flextimer: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape flextimer support.
|
||||
|
||||
Signed-off-by: Wang Dongsheng <dongsheng.wang@nxp.com>
|
||||
Signed-off-by: Meng Yi <meng.yi@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/clocksource/fsl_ftm_timer.c | 8 +-
|
||||
drivers/soc/fsl/layerscape/ftm_alarm.c | 367 +++++++++++++++++++++++++++++++++
|
||||
2 files changed, 371 insertions(+), 4 deletions(-)
|
||||
create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
|
||||
|
||||
--- a/drivers/clocksource/fsl_ftm_timer.c
|
||||
+++ b/drivers/clocksource/fsl_ftm_timer.c
|
||||
@@ -83,11 +83,11 @@ static inline void ftm_counter_disable(v
|
||||
|
||||
static inline void ftm_irq_acknowledge(void __iomem *base)
|
||||
{
|
||||
- u32 val;
|
||||
+ unsigned int timeout = 100;
|
||||
|
||||
- val = ftm_readl(base + FTM_SC);
|
||||
- val &= ~FTM_SC_TOF;
|
||||
- ftm_writel(val, base + FTM_SC);
|
||||
+ while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
|
||||
+ ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
|
||||
+ base + FTM_SC);
|
||||
}
|
||||
|
||||
static inline void ftm_irq_enable(void __iomem *base)
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
|
||||
@@ -0,0 +1,367 @@
|
||||
+/*
|
||||
+ * Freescale FlexTimer Module (FTM) Alarm driver.
|
||||
+ *
|
||||
+ * Copyright 2014 Freescale Semiconductor, Inc.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2
|
||||
+ * of the License, or (at your option) any later version.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/libata.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#define FTM_SC 0x00
|
||||
+#define FTM_SC_CLK_SHIFT 3
|
||||
+#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
|
||||
+#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
|
||||
+#define FTM_SC_PS_MASK 0x7
|
||||
+#define FTM_SC_TOIE BIT(6)
|
||||
+#define FTM_SC_TOF BIT(7)
|
||||
+
|
||||
+#define FTM_SC_CLKS_FIXED_FREQ 0x02
|
||||
+
|
||||
+#define FTM_CNT 0x04
|
||||
+#define FTM_MOD 0x08
|
||||
+#define FTM_CNTIN 0x4C
|
||||
+
|
||||
+#define FIXED_FREQ_CLK 32000
|
||||
+#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
|
||||
+#define MAX_COUNT_VAL 0xffff
|
||||
+
|
||||
+static void __iomem *ftm1_base;
|
||||
+static void __iomem *rcpm_ftm_addr;
|
||||
+static u32 alarm_freq;
|
||||
+static bool big_endian;
|
||||
+
|
||||
+enum pmu_endian_type {
|
||||
+ BIG_ENDIAN,
|
||||
+ LITTLE_ENDIAN,
|
||||
+};
|
||||
+
|
||||
+struct rcpm_cfg {
|
||||
+ enum pmu_endian_type big_endian; /* Big/Little endian of PMU module */
|
||||
+ u32 flextimer_set_bit; /* FlexTimer1 is not powerdown during device LPM20 */
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_cfg ls1012a_rcpm_cfg = {
|
||||
+ .big_endian = BIG_ENDIAN,
|
||||
+ .flextimer_set_bit = 0x20000,
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_cfg ls1021a_rcpm_cfg = {
|
||||
+ .big_endian = BIG_ENDIAN,
|
||||
+ .flextimer_set_bit = 0x20000,
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_cfg ls1043a_rcpm_cfg = {
|
||||
+ .big_endian = BIG_ENDIAN,
|
||||
+ .flextimer_set_bit = 0x20000,
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_cfg ls1046a_rcpm_cfg = {
|
||||
+ .big_endian = BIG_ENDIAN,
|
||||
+ .flextimer_set_bit = 0x20000,
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_cfg ls1088a_rcpm_cfg = {
|
||||
+ .big_endian = LITTLE_ENDIAN,
|
||||
+ .flextimer_set_bit = 0x4000,
|
||||
+};
|
||||
+
|
||||
+static struct rcpm_cfg ls208xa_rcpm_cfg = {
|
||||
+ .big_endian = LITTLE_ENDIAN,
|
||||
+ .flextimer_set_bit = 0x4000,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id ippdexpcr_of_match[] = {
|
||||
+ { .compatible = "fsl,ls1012a-ftm", .data = &ls1012a_rcpm_cfg},
|
||||
+ { .compatible = "fsl,ls1021a-ftm", .data = &ls1021a_rcpm_cfg},
|
||||
+ { .compatible = "fsl,ls1043a-ftm", .data = &ls1043a_rcpm_cfg},
|
||||
+ { .compatible = "fsl,ls1046a-ftm", .data = &ls1046a_rcpm_cfg},
|
||||
+ { .compatible = "fsl,ls1088a-ftm", .data = &ls1088a_rcpm_cfg},
|
||||
+ { .compatible = "fsl,ls208xa-ftm", .data = &ls208xa_rcpm_cfg},
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, ippdexpcr_of_match);
|
||||
+
|
||||
+static inline u32 ftm_readl(void __iomem *addr)
|
||||
+{
|
||||
+ if (big_endian)
|
||||
+ return ioread32be(addr);
|
||||
+
|
||||
+ return ioread32(addr);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_writel(u32 val, void __iomem *addr)
|
||||
+{
|
||||
+ if (big_endian)
|
||||
+ iowrite32be(val, addr);
|
||||
+ else
|
||||
+ iowrite32(val, addr);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_counter_enable(void __iomem *base)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* select and enable counter clock source */
|
||||
+ val = ftm_readl(base + FTM_SC);
|
||||
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
|
||||
+ val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
|
||||
+ ftm_writel(val, base + FTM_SC);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_counter_disable(void __iomem *base)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* disable counter clock source */
|
||||
+ val = ftm_readl(base + FTM_SC);
|
||||
+ val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
|
||||
+ ftm_writel(val, base + FTM_SC);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_irq_acknowledge(void __iomem *base)
|
||||
+{
|
||||
+ unsigned int timeout = 100;
|
||||
+
|
||||
+ while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
|
||||
+ ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
|
||||
+ base + FTM_SC);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_irq_enable(void __iomem *base)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = ftm_readl(base + FTM_SC);
|
||||
+ val |= FTM_SC_TOIE;
|
||||
+ ftm_writel(val, base + FTM_SC);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_irq_disable(void __iomem *base)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = ftm_readl(base + FTM_SC);
|
||||
+ val &= ~FTM_SC_TOIE;
|
||||
+ ftm_writel(val, base + FTM_SC);
|
||||
+}
|
||||
+
|
||||
+static inline void ftm_reset_counter(void __iomem *base)
|
||||
+{
|
||||
+ /*
|
||||
+ * The CNT register contains the FTM counter value.
|
||||
+ * Reset clears the CNT register. Writing any value to COUNT
|
||||
+ * updates the counter with its initial value, CNTIN.
|
||||
+ */
|
||||
+ ftm_writel(0x00, base + FTM_CNT);
|
||||
+}
|
||||
+
|
||||
+static u32 time_to_cycle(unsigned long time)
|
||||
+{
|
||||
+ u32 cycle;
|
||||
+
|
||||
+ cycle = time * alarm_freq;
|
||||
+ if (cycle > MAX_COUNT_VAL) {
|
||||
+ pr_err("Out of alarm range.\n");
|
||||
+ cycle = 0;
|
||||
+ }
|
||||
+
|
||||
+ return cycle;
|
||||
+}
|
||||
+
|
||||
+static u32 cycle_to_time(u32 cycle)
|
||||
+{
|
||||
+ return cycle / alarm_freq + 1;
|
||||
+}
|
||||
+
|
||||
+static void ftm_clean_alarm(void)
|
||||
+{
|
||||
+ ftm_counter_disable(ftm1_base);
|
||||
+
|
||||
+ ftm_writel(0x00, ftm1_base + FTM_CNTIN);
|
||||
+ ftm_writel(~0U, ftm1_base + FTM_MOD);
|
||||
+
|
||||
+ ftm_reset_counter(ftm1_base);
|
||||
+}
|
||||
+
|
||||
+static int ftm_set_alarm(u64 cycle)
|
||||
+{
|
||||
+ ftm_irq_disable(ftm1_base);
|
||||
+
|
||||
+ /*
|
||||
+ * The counter increments until the value of MOD is reached,
|
||||
+ * at which point the counter is reloaded with the value of CNTIN.
|
||||
+ * The TOF (the overflow flag) bit is set when the FTM counter
|
||||
+ * changes from MOD to CNTIN. So we should using the cycle - 1.
|
||||
+ */
|
||||
+ ftm_writel(cycle - 1, ftm1_base + FTM_MOD);
|
||||
+
|
||||
+ ftm_counter_enable(ftm1_base);
|
||||
+
|
||||
+ ftm_irq_enable(ftm1_base);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id)
|
||||
+{
|
||||
+ ftm_irq_acknowledge(ftm1_base);
|
||||
+ ftm_irq_disable(ftm1_base);
|
||||
+ ftm_clean_alarm();
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static ssize_t ftm_alarm_show(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ char *buf)
|
||||
+{
|
||||
+ u32 count, val;
|
||||
+
|
||||
+ count = ftm_readl(ftm1_base + FTM_MOD);
|
||||
+ val = ftm_readl(ftm1_base + FTM_CNT);
|
||||
+ val = (count & MAX_COUNT_VAL) - val;
|
||||
+ val = cycle_to_time(val);
|
||||
+
|
||||
+ return sprintf(buf, "%u\n", val);
|
||||
+}
|
||||
+
|
||||
+static ssize_t ftm_alarm_store(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ const char *buf, size_t count)
|
||||
+{
|
||||
+ u32 cycle;
|
||||
+ unsigned long time;
|
||||
+
|
||||
+ if (kstrtoul(buf, 0, &time))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ftm_clean_alarm();
|
||||
+
|
||||
+ cycle = time_to_cycle(time);
|
||||
+ if (!cycle)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ftm_set_alarm(cycle);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644,
|
||||
+ ftm_alarm_show, ftm_alarm_store);
|
||||
+
|
||||
+static int ftm_alarm_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct resource *r;
|
||||
+ int irq;
|
||||
+ int ret;
|
||||
+ struct rcpm_cfg *rcpm_cfg;
|
||||
+ u32 ippdexpcr, flextimer;
|
||||
+ const struct of_device_id *of_id;
|
||||
+ enum pmu_endian_type endian;
|
||||
+
|
||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ if (!r)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ ftm1_base = devm_ioremap_resource(&pdev->dev, r);
|
||||
+ if (IS_ERR(ftm1_base))
|
||||
+ return PTR_ERR(ftm1_base);
|
||||
+
|
||||
+ of_id = of_match_node(ippdexpcr_of_match, np);
|
||||
+ if (!of_id)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ rcpm_cfg = devm_kzalloc(&pdev->dev, sizeof(*rcpm_cfg), GFP_KERNEL);
|
||||
+ if (!rcpm_cfg)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ rcpm_cfg = (struct rcpm_cfg*)of_id->data;
|
||||
+ endian = rcpm_cfg->big_endian;
|
||||
+ flextimer = rcpm_cfg->flextimer_set_bit;
|
||||
+
|
||||
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "FlexTimer1");
|
||||
+ if (r) {
|
||||
+ rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
|
||||
+ if (IS_ERR(rcpm_ftm_addr))
|
||||
+ return PTR_ERR(rcpm_ftm_addr);
|
||||
+ if (endian == BIG_ENDIAN)
|
||||
+ ippdexpcr = ioread32be(rcpm_ftm_addr);
|
||||
+ else
|
||||
+ ippdexpcr = ioread32(rcpm_ftm_addr);
|
||||
+ ippdexpcr |= flextimer;
|
||||
+ if (endian == BIG_ENDIAN)
|
||||
+ iowrite32be(ippdexpcr, rcpm_ftm_addr);
|
||||
+ else
|
||||
+ iowrite32(ippdexpcr, rcpm_ftm_addr);
|
||||
+ }
|
||||
+
|
||||
+ irq = irq_of_parse_and_map(np, 0);
|
||||
+ if (irq <= 0) {
|
||||
+ pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ big_endian = of_property_read_bool(np, "big-endian");
|
||||
+
|
||||
+ ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt,
|
||||
+ IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&pdev->dev, "failed to request irq\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = device_create_file(&pdev->dev, &ftm_alarm_attributes);
|
||||
+ if (ret) {
|
||||
+ dev_err(&pdev->dev, "create sysfs fail.\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
|
||||
+
|
||||
+ ftm_clean_alarm();
|
||||
+
|
||||
+ device_init_wakeup(&pdev->dev, true);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id ftm_alarm_match[] = {
|
||||
+ { .compatible = "fsl,ls1012a-ftm", },
|
||||
+ { .compatible = "fsl,ls1021a-ftm", },
|
||||
+ { .compatible = "fsl,ls1043a-ftm", },
|
||||
+ { .compatible = "fsl,ls1046a-ftm", },
|
||||
+ { .compatible = "fsl,ls1088a-ftm", },
|
||||
+ { .compatible = "fsl,ls208xa-ftm", },
|
||||
+ { .compatible = "fsl,ftm-timer", },
|
||||
+ { },
|
||||
+};
|
||||
+
|
||||
+static struct platform_driver ftm_alarm_driver = {
|
||||
+ .probe = ftm_alarm_probe,
|
||||
+ .driver = {
|
||||
+ .name = "ftm-alarm",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = ftm_alarm_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init ftm_alarm_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&ftm_alarm_driver);
|
||||
+}
|
||||
+device_initcall(ftm_alarm_init);
|
||||
@@ -0,0 +1,58 @@
|
||||
From 177f92a14d8177124f37db0fafc11182e2dcdd62 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:33:05 +0800
|
||||
Subject: [PATCH 19/30] gpu: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape dcu support.
|
||||
|
||||
Signed-off-by: Alison Wang <b18965@freescale.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 15 ++++++++++++++-
|
||||
1 file changed, 14 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
|
||||
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
|
||||
@@ -225,7 +225,6 @@ static int fsl_dcu_drm_pm_suspend(struct
|
||||
if (!fsl_dev)
|
||||
return 0;
|
||||
|
||||
- disable_irq(fsl_dev->irq);
|
||||
drm_kms_helper_poll_disable(fsl_dev->drm);
|
||||
|
||||
console_lock();
|
||||
@@ -243,6 +242,8 @@ static int fsl_dcu_drm_pm_suspend(struct
|
||||
return PTR_ERR(fsl_dev->state);
|
||||
}
|
||||
|
||||
+ disable_irq(fsl_dev->irq);
|
||||
+
|
||||
clk_disable_unprepare(fsl_dev->clk);
|
||||
|
||||
return 0;
|
||||
@@ -262,6 +263,12 @@ static int fsl_dcu_drm_pm_resume(struct
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ ret = clk_prepare_enable(fsl_dev->pix_clk);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev, "failed to enable dcu pix clk\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
if (fsl_dev->tcon)
|
||||
fsl_tcon_bypass_enable(fsl_dev->tcon);
|
||||
fsl_dcu_drm_init_planes(fsl_dev->drm);
|
||||
@@ -388,6 +395,12 @@ static int fsl_dcu_drm_probe(struct plat
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
+ ret = clk_prepare_enable(fsl_dev->pix_clk);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev, "failed to enable dcu pix clk\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
fsl_dev->tcon = fsl_tcon_init(dev);
|
||||
|
||||
drm = drm_dev_alloc(driver, dev);
|
||||
@@ -0,0 +1,421 @@
|
||||
From 45b0e1589b25ea3106a8c8d18bf653fde95baa9f Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:34:22 +0800
|
||||
Subject: [PATCH 20/30] guts: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape guts support.
|
||||
|
||||
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
|
||||
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
Signed-off-by: Amrita Kumari <amrita.kumari@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/soc/fsl/guts.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/fsl/guts.h | 125 +++++++++++++++----------
|
||||
2 files changed, 315 insertions(+), 48 deletions(-)
|
||||
create mode 100644 drivers/soc/fsl/guts.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/soc/fsl/guts.c
|
||||
@@ -0,0 +1,238 @@
|
||||
+/*
|
||||
+ * Freescale QorIQ Platforms GUTS Driver
|
||||
+ *
|
||||
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of_fdt.h>
|
||||
+#include <linux/sys_soc.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/fsl/guts.h>
|
||||
+
|
||||
+struct guts {
|
||||
+ struct ccsr_guts __iomem *regs;
|
||||
+ bool little_endian;
|
||||
+};
|
||||
+
|
||||
+struct fsl_soc_die_attr {
|
||||
+ char *die;
|
||||
+ u32 svr;
|
||||
+ u32 mask;
|
||||
+};
|
||||
+
|
||||
+static struct guts *guts;
|
||||
+static struct soc_device_attribute soc_dev_attr;
|
||||
+static struct soc_device *soc_dev;
|
||||
+
|
||||
+
|
||||
+/* SoC die attribute definition for QorIQ platform */
|
||||
+static const struct fsl_soc_die_attr fsl_soc_die[] = {
|
||||
+ /*
|
||||
+ * Power Architecture-based SoCs T Series
|
||||
+ */
|
||||
+
|
||||
+ /* Die: T4240, SoC: T4240/T4160/T4080 */
|
||||
+ { .die = "T4240",
|
||||
+ .svr = 0x82400000,
|
||||
+ .mask = 0xfff00000,
|
||||
+ },
|
||||
+ /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */
|
||||
+ { .die = "T1040",
|
||||
+ .svr = 0x85200000,
|
||||
+ .mask = 0xfff00000,
|
||||
+ },
|
||||
+ /* Die: T2080, SoC: T2080/T2081 */
|
||||
+ { .die = "T2080",
|
||||
+ .svr = 0x85300000,
|
||||
+ .mask = 0xfff00000,
|
||||
+ },
|
||||
+ /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */
|
||||
+ { .die = "T1024",
|
||||
+ .svr = 0x85400000,
|
||||
+ .mask = 0xfff00000,
|
||||
+ },
|
||||
+
|
||||
+ /*
|
||||
+ * ARM-based SoCs LS Series
|
||||
+ */
|
||||
+
|
||||
+ /* Die: LS1043A, SoC: LS1043A/LS1023A */
|
||||
+ { .die = "LS1043A",
|
||||
+ .svr = 0x87920000,
|
||||
+ .mask = 0xffff0000,
|
||||
+ },
|
||||
+ /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */
|
||||
+ { .die = "LS2080A",
|
||||
+ .svr = 0x87010000,
|
||||
+ .mask = 0xff3f0000,
|
||||
+ },
|
||||
+ /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */
|
||||
+ { .die = "LS1088A",
|
||||
+ .svr = 0x87030000,
|
||||
+ .mask = 0xff3f0000,
|
||||
+ },
|
||||
+ /* Die: LS1012A, SoC: LS1012A */
|
||||
+ { .die = "LS1012A",
|
||||
+ .svr = 0x87040000,
|
||||
+ .mask = 0xffff0000,
|
||||
+ },
|
||||
+ /* Die: LS1046A, SoC: LS1046A/LS1026A */
|
||||
+ { .die = "LS1046A",
|
||||
+ .svr = 0x87070000,
|
||||
+ .mask = 0xffff0000,
|
||||
+ },
|
||||
+ /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */
|
||||
+ { .die = "LS2088A",
|
||||
+ .svr = 0x87090000,
|
||||
+ .mask = 0xff3f0000,
|
||||
+ },
|
||||
+ /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */
|
||||
+ { .die = "LS1021A",
|
||||
+ .svr = 0x87000000,
|
||||
+ .mask = 0xfff70000,
|
||||
+ },
|
||||
+ { },
|
||||
+};
|
||||
+
|
||||
+static const struct fsl_soc_die_attr *fsl_soc_die_match(
|
||||
+ u32 svr, const struct fsl_soc_die_attr *matches)
|
||||
+{
|
||||
+ while (matches->svr) {
|
||||
+ if (matches->svr == (svr & matches->mask))
|
||||
+ return matches;
|
||||
+ matches++;
|
||||
+ };
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+u32 fsl_guts_get_svr(void)
|
||||
+{
|
||||
+ u32 svr = 0;
|
||||
+
|
||||
+ if (!guts || !guts->regs)
|
||||
+ return svr;
|
||||
+
|
||||
+ if (guts->little_endian)
|
||||
+ svr = ioread32(&guts->regs->svr);
|
||||
+ else
|
||||
+ svr = ioread32be(&guts->regs->svr);
|
||||
+
|
||||
+ return svr;
|
||||
+}
|
||||
+EXPORT_SYMBOL(fsl_guts_get_svr);
|
||||
+
|
||||
+static int fsl_guts_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct resource *res;
|
||||
+ const struct fsl_soc_die_attr *soc_die;
|
||||
+ const char *machine;
|
||||
+ u32 svr;
|
||||
+
|
||||
+ /* Initialize guts */
|
||||
+ guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL);
|
||||
+ if (!guts)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ guts->little_endian = of_property_read_bool(np, "little-endian");
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ guts->regs = devm_ioremap_resource(dev, res);
|
||||
+ if (IS_ERR(guts->regs))
|
||||
+ return PTR_ERR(guts->regs);
|
||||
+
|
||||
+ /* Register soc device */
|
||||
+ machine = of_flat_dt_get_machine_name();
|
||||
+ if (machine)
|
||||
+ soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL);
|
||||
+
|
||||
+ svr = fsl_guts_get_svr();
|
||||
+ soc_die = fsl_soc_die_match(svr, fsl_soc_die);
|
||||
+ if (soc_die) {
|
||||
+ soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL,
|
||||
+ "QorIQ %s", soc_die->die);
|
||||
+ } else {
|
||||
+ soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "QorIQ");
|
||||
+ }
|
||||
+ soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL,
|
||||
+ "svr:0x%08x", svr);
|
||||
+ soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d",
|
||||
+ (svr >> 4) & 0xf, svr & 0xf);
|
||||
+
|
||||
+ soc_dev = soc_device_register(&soc_dev_attr);
|
||||
+ if (IS_ERR(soc_dev))
|
||||
+ return PTR_ERR(soc_dev);
|
||||
+
|
||||
+ pr_info("Machine: %s\n", soc_dev_attr.machine);
|
||||
+ pr_info("SoC family: %s\n", soc_dev_attr.family);
|
||||
+ pr_info("SoC ID: %s, Revision: %s\n",
|
||||
+ soc_dev_attr.soc_id, soc_dev_attr.revision);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int fsl_guts_remove(struct platform_device *dev)
|
||||
+{
|
||||
+ soc_device_unregister(soc_dev);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Table for matching compatible strings, for device tree
|
||||
+ * guts node, for Freescale QorIQ SOCs.
|
||||
+ */
|
||||
+static const struct of_device_id fsl_guts_of_match[] = {
|
||||
+ { .compatible = "fsl,qoriq-device-config-1.0", },
|
||||
+ { .compatible = "fsl,qoriq-device-config-2.0", },
|
||||
+ { .compatible = "fsl,p1010-guts", },
|
||||
+ { .compatible = "fsl,p1020-guts", },
|
||||
+ { .compatible = "fsl,p1021-guts", },
|
||||
+ { .compatible = "fsl,p1022-guts", },
|
||||
+ { .compatible = "fsl,p1023-guts", },
|
||||
+ { .compatible = "fsl,p2020-guts", },
|
||||
+ { .compatible = "fsl,bsc9131-guts", },
|
||||
+ { .compatible = "fsl,bsc9132-guts", },
|
||||
+ { .compatible = "fsl,mpc8536-guts", },
|
||||
+ { .compatible = "fsl,mpc8544-guts", },
|
||||
+ { .compatible = "fsl,mpc8548-guts", },
|
||||
+ { .compatible = "fsl,mpc8568-guts", },
|
||||
+ { .compatible = "fsl,mpc8569-guts", },
|
||||
+ { .compatible = "fsl,mpc8572-guts", },
|
||||
+ { .compatible = "fsl,ls1021a-dcfg", },
|
||||
+ { .compatible = "fsl,ls1043a-dcfg", },
|
||||
+ { .compatible = "fsl,ls1046a-dcfg", },
|
||||
+ { .compatible = "fsl,ls2080a-dcfg", },
|
||||
+ { .compatible = "fsl,ls1088a-dcfg", },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, fsl_guts_of_match);
|
||||
+
|
||||
+static struct platform_driver fsl_guts_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "fsl-guts",
|
||||
+ .of_match_table = fsl_guts_of_match,
|
||||
+ },
|
||||
+ .probe = fsl_guts_probe,
|
||||
+ .remove = fsl_guts_remove,
|
||||
+};
|
||||
+
|
||||
+static int __init fsl_guts_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&fsl_guts_driver);
|
||||
+}
|
||||
+core_initcall(fsl_guts_init);
|
||||
+
|
||||
+static void __exit fsl_guts_exit(void)
|
||||
+{
|
||||
+ platform_driver_unregister(&fsl_guts_driver);
|
||||
+}
|
||||
+module_exit(fsl_guts_exit);
|
||||
--- a/include/linux/fsl/guts.h
|
||||
+++ b/include/linux/fsl/guts.h
|
||||
@@ -30,83 +30,112 @@
|
||||
* #ifdefs.
|
||||
*/
|
||||
struct ccsr_guts {
|
||||
- __be32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */
|
||||
- __be32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */
|
||||
- __be32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */
|
||||
- __be32 pordevsr; /* 0x.000c - POR I/O Device Status Register */
|
||||
- __be32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */
|
||||
- __be32 pordevsr2; /* 0x.0014 - POR device status register 2 */
|
||||
+ u32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */
|
||||
+ u32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */
|
||||
+ u32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and
|
||||
+ * Control Register
|
||||
+ */
|
||||
+ u32 pordevsr; /* 0x.000c - POR I/O Device Status Register */
|
||||
+ u32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */
|
||||
+ u32 pordevsr2; /* 0x.0014 - POR device status register 2 */
|
||||
u8 res018[0x20 - 0x18];
|
||||
- __be32 porcir; /* 0x.0020 - POR Configuration Information Register */
|
||||
+ u32 porcir; /* 0x.0020 - POR Configuration Information
|
||||
+ * Register
|
||||
+ */
|
||||
u8 res024[0x30 - 0x24];
|
||||
- __be32 gpiocr; /* 0x.0030 - GPIO Control Register */
|
||||
+ u32 gpiocr; /* 0x.0030 - GPIO Control Register */
|
||||
u8 res034[0x40 - 0x34];
|
||||
- __be32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */
|
||||
+ u32 gpoutdr; /* 0x.0040 - General-Purpose Output Data
|
||||
+ * Register
|
||||
+ */
|
||||
u8 res044[0x50 - 0x44];
|
||||
- __be32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */
|
||||
+ u32 gpindr; /* 0x.0050 - General-Purpose Input Data
|
||||
+ * Register
|
||||
+ */
|
||||
u8 res054[0x60 - 0x54];
|
||||
- __be32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */
|
||||
- __be32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */
|
||||
- __be32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */
|
||||
+ u32 pmuxcr; /* 0x.0060 - Alternate Function Signal
|
||||
+ * Multiplex Control
|
||||
+ */
|
||||
+ u32 pmuxcr2; /* 0x.0064 - Alternate function signal
|
||||
+ * multiplex control 2
|
||||
+ */
|
||||
+ u32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */
|
||||
u8 res06c[0x70 - 0x6c];
|
||||
- __be32 devdisr; /* 0x.0070 - Device Disable Control */
|
||||
+ u32 devdisr; /* 0x.0070 - Device Disable Control */
|
||||
#define CCSR_GUTS_DEVDISR_TB1 0x00001000
|
||||
#define CCSR_GUTS_DEVDISR_TB0 0x00004000
|
||||
- __be32 devdisr2; /* 0x.0074 - Device Disable Control 2 */
|
||||
+ u32 devdisr2; /* 0x.0074 - Device Disable Control 2 */
|
||||
u8 res078[0x7c - 0x78];
|
||||
- __be32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */
|
||||
- __be32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */
|
||||
- __be32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */
|
||||
- __be32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */
|
||||
- __be32 pmcdr; /* 0x.008c - 4Power management clock disable register */
|
||||
- __be32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */
|
||||
- __be32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */
|
||||
- __be32 ectrstcr; /* 0x.0098 - Exception reset control register */
|
||||
- __be32 autorstsr; /* 0x.009c - Automatic reset status register */
|
||||
- __be32 pvr; /* 0x.00a0 - Processor Version Register */
|
||||
- __be32 svr; /* 0x.00a4 - System Version Register */
|
||||
+ u32 pmjcr; /* 0x.007c - 4 Power Management Jog Control
|
||||
+ * Register
|
||||
+ */
|
||||
+ u32 powmgtcsr; /* 0x.0080 - Power Management Status and
|
||||
+ * Control Register
|
||||
+ */
|
||||
+ u32 pmrccr; /* 0x.0084 - Power Management Reset Counter
|
||||
+ * Configuration Register
|
||||
+ */
|
||||
+ u32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter
|
||||
+ * Configuration Register
|
||||
+ */
|
||||
+ u32 pmcdr; /* 0x.008c - 4Power management clock disable
|
||||
+ * register
|
||||
+ */
|
||||
+ u32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */
|
||||
+ u32 rstrscr; /* 0x.0094 - Reset Request Status and
|
||||
+ * Control Register
|
||||
+ */
|
||||
+ u32 ectrstcr; /* 0x.0098 - Exception reset control register */
|
||||
+ u32 autorstsr; /* 0x.009c - Automatic reset status register */
|
||||
+ u32 pvr; /* 0x.00a0 - Processor Version Register */
|
||||
+ u32 svr; /* 0x.00a4 - System Version Register */
|
||||
u8 res0a8[0xb0 - 0xa8];
|
||||
- __be32 rstcr; /* 0x.00b0 - Reset Control Register */
|
||||
+ u32 rstcr; /* 0x.00b0 - Reset Control Register */
|
||||
u8 res0b4[0xc0 - 0xb4];
|
||||
- __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register
|
||||
+ u32 iovselsr; /* 0x.00c0 - I/O voltage select status register
|
||||
Called 'elbcvselcr' on 86xx SOCs */
|
||||
u8 res0c4[0x100 - 0xc4];
|
||||
- __be32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers
|
||||
+ u32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers
|
||||
There are 16 registers */
|
||||
u8 res140[0x224 - 0x140];
|
||||
- __be32 iodelay1; /* 0x.0224 - IO delay control register 1 */
|
||||
- __be32 iodelay2; /* 0x.0228 - IO delay control register 2 */
|
||||
+ u32 iodelay1; /* 0x.0224 - IO delay control register 1 */
|
||||
+ u32 iodelay2; /* 0x.0228 - IO delay control register 2 */
|
||||
u8 res22c[0x604 - 0x22c];
|
||||
- __be32 pamubypenr; /* 0x.604 - PAMU bypass enable register */
|
||||
+ u32 pamubypenr; /* 0x.604 - PAMU bypass enable register */
|
||||
u8 res608[0x800 - 0x608];
|
||||
- __be32 clkdvdr; /* 0x.0800 - Clock Divide Register */
|
||||
+ u32 clkdvdr; /* 0x.0800 - Clock Divide Register */
|
||||
u8 res804[0x900 - 0x804];
|
||||
- __be32 ircr; /* 0x.0900 - Infrared Control Register */
|
||||
+ u32 ircr; /* 0x.0900 - Infrared Control Register */
|
||||
u8 res904[0x908 - 0x904];
|
||||
- __be32 dmacr; /* 0x.0908 - DMA Control Register */
|
||||
+ u32 dmacr; /* 0x.0908 - DMA Control Register */
|
||||
u8 res90c[0x914 - 0x90c];
|
||||
- __be32 elbccr; /* 0x.0914 - eLBC Control Register */
|
||||
+ u32 elbccr; /* 0x.0914 - eLBC Control Register */
|
||||
u8 res918[0xb20 - 0x918];
|
||||
- __be32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */
|
||||
- __be32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */
|
||||
- __be32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */
|
||||
+ u32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */
|
||||
+ u32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */
|
||||
+ u32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */
|
||||
u8 resb2c[0xe00 - 0xb2c];
|
||||
- __be32 clkocr; /* 0x.0e00 - Clock Out Select Register */
|
||||
+ u32 clkocr; /* 0x.0e00 - Clock Out Select Register */
|
||||
u8 rese04[0xe10 - 0xe04];
|
||||
- __be32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */
|
||||
+ u32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */
|
||||
u8 rese14[0xe20 - 0xe14];
|
||||
- __be32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */
|
||||
- __be32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */
|
||||
+ u32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */
|
||||
+ u32 cpfor; /* 0x.0e24 - L2 charge pump fuse override
|
||||
+ * register
|
||||
+ */
|
||||
u8 rese28[0xf04 - 0xe28];
|
||||
- __be32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */
|
||||
- __be32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */
|
||||
+ u32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */
|
||||
+ u32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */
|
||||
u8 resf0c[0xf2c - 0xf0c];
|
||||
- __be32 itcr; /* 0x.0f2c - Internal transaction control register */
|
||||
+ u32 itcr; /* 0x.0f2c - Internal transaction control
|
||||
+ * register
|
||||
+ */
|
||||
u8 resf30[0xf40 - 0xf30];
|
||||
- __be32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */
|
||||
- __be32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */
|
||||
+ u32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */
|
||||
+ u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
+u32 fsl_guts_get_svr(void);
|
||||
|
||||
/* Alternate function signal multiplex control */
|
||||
#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x))
|
||||
@@ -0,0 +1,360 @@
|
||||
From 659aa30c59fb188b533a7edcb9bd38ac007a2739 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:35:11 +0800
|
||||
Subject: [PATCH 21/30] i2c: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape i2c support.
|
||||
|
||||
Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
|
||||
Signed-off-by: Priyanka Jain <Priyanka.Jain@freescale.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/i2c/busses/i2c-imx.c | 195 +++++++++++++++++++++++++++++++++++-
|
||||
drivers/i2c/muxes/i2c-mux-pca954x.c | 43 ++++++++
|
||||
2 files changed, 237 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-imx.c
|
||||
+++ b/drivers/i2c/busses/i2c-imx.c
|
||||
@@ -53,6 +53,11 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/libata.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "imx-i2c"
|
||||
@@ -117,6 +122,54 @@
|
||||
|
||||
#define I2C_PM_TIMEOUT 10 /* ms */
|
||||
|
||||
+enum pinmux_endian_type {
|
||||
+ BIG_ENDIAN,
|
||||
+ LITTLE_ENDIAN,
|
||||
+};
|
||||
+
|
||||
+struct pinmux_cfg {
|
||||
+ enum pinmux_endian_type endian; /* endian of RCWPMUXCR0 */
|
||||
+ u32 pmuxcr_offset;
|
||||
+ u32 pmuxcr_set_bit; /* pin mux of RCWPMUXCR0 */
|
||||
+};
|
||||
+
|
||||
+static struct pinmux_cfg ls1012a_pinmux_cfg = {
|
||||
+ .endian = BIG_ENDIAN,
|
||||
+ .pmuxcr_offset = 0x430,
|
||||
+ .pmuxcr_set_bit = 0x10,
|
||||
+};
|
||||
+
|
||||
+static struct pinmux_cfg ls1043a_pinmux_cfg = {
|
||||
+ .endian = BIG_ENDIAN,
|
||||
+ .pmuxcr_offset = 0x40C,
|
||||
+ .pmuxcr_set_bit = 0x10,
|
||||
+};
|
||||
+
|
||||
+static struct pinmux_cfg ls1046a_pinmux_cfg = {
|
||||
+ .endian = BIG_ENDIAN,
|
||||
+ .pmuxcr_offset = 0x40C,
|
||||
+ .pmuxcr_set_bit = 0x80000000,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id pinmux_of_match[] = {
|
||||
+ { .compatible = "fsl,ls1012a-vf610-i2c", .data = &ls1012a_pinmux_cfg},
|
||||
+ { .compatible = "fsl,ls1043a-vf610-i2c", .data = &ls1043a_pinmux_cfg},
|
||||
+ { .compatible = "fsl,ls1046a-vf610-i2c", .data = &ls1046a_pinmux_cfg},
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, pinmux_of_match);
|
||||
+
|
||||
+/* The SCFG, Supplemental Configuration Unit, provides SoC specific
|
||||
+ * configuration and status registers for the device. There is a
|
||||
+ * SDHC IO VSEL control register on SCFG for some platforms. It's
|
||||
+ * used to support SDHC IO voltage switching.
|
||||
+ */
|
||||
+static const struct of_device_id scfg_device_ids[] = {
|
||||
+ { .compatible = "fsl,ls1012a-scfg", },
|
||||
+ { .compatible = "fsl,ls1043a-scfg", },
|
||||
+ { .compatible = "fsl,ls1046a-scfg", },
|
||||
+ {}
|
||||
+};
|
||||
/*
|
||||
* sorted list of clock divider, register value pairs
|
||||
* taken from table 26-5, p.26-9, Freescale i.MX
|
||||
@@ -210,6 +263,12 @@ struct imx_i2c_struct {
|
||||
struct pinctrl_state *pinctrl_pins_gpio;
|
||||
|
||||
struct imx_i2c_dma *dma;
|
||||
+ int layerscape_bus_recover;
|
||||
+ int gpio;
|
||||
+ int need_set_pmuxcr;
|
||||
+ int pmuxcr_set;
|
||||
+ int pmuxcr_endian;
|
||||
+ void __iomem *pmuxcr_addr;
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
@@ -878,6 +937,78 @@ static int i2c_imx_read(struct imx_i2c_s
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Based on the I2C specification, if the data line (SDA) is
|
||||
+ * stuck low, the master should send nine * clock pulses.
|
||||
+ * The I2C slave device that held the bus low should release it
|
||||
+ * sometime within * those nine clocks. Due to this erratum,
|
||||
+ * the I2C controller cannot generate nine clock pulses.
|
||||
+ */
|
||||
+static int i2c_imx_recovery_for_layerscape(struct imx_i2c_struct *i2c_imx)
|
||||
+{
|
||||
+ u32 pmuxcr = 0;
|
||||
+ int ret;
|
||||
+ unsigned int i, temp;
|
||||
+
|
||||
+ /* configure IICx_SCL/GPIO pin as a GPIO */
|
||||
+ if (i2c_imx->need_set_pmuxcr == 1) {
|
||||
+ pmuxcr = ioread32be(i2c_imx->pmuxcr_addr);
|
||||
+ if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
|
||||
+ iowrite32be(i2c_imx->pmuxcr_set|pmuxcr,
|
||||
+ i2c_imx->pmuxcr_addr);
|
||||
+ else
|
||||
+ iowrite32(i2c_imx->pmuxcr_set|pmuxcr,
|
||||
+ i2c_imx->pmuxcr_addr);
|
||||
+ }
|
||||
+
|
||||
+ ret = gpio_request(i2c_imx->gpio, i2c_imx->adapter.name);
|
||||
+ if (ret) {
|
||||
+ dev_err(&i2c_imx->adapter.dev,
|
||||
+ "can't get gpio: %d\n", ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Configure GPIO pin as an output and open drain. */
|
||||
+ gpio_direction_output(i2c_imx->gpio, 1);
|
||||
+ udelay(10);
|
||||
+
|
||||
+ /* Write data to generate 9 pulses */
|
||||
+ for (i = 0; i < 9; i++) {
|
||||
+ gpio_set_value(i2c_imx->gpio, 1);
|
||||
+ udelay(10);
|
||||
+ gpio_set_value(i2c_imx->gpio, 0);
|
||||
+ udelay(10);
|
||||
+ }
|
||||
+ /* ensure that the last level sent is always high */
|
||||
+ gpio_set_value(i2c_imx->gpio, 1);
|
||||
+
|
||||
+ /*
|
||||
+ * Set I2Cx_IBCR = 0h00 to generate a STOP and then
|
||||
+ * set I2Cx_IBCR = 0h80 to reset
|
||||
+ */
|
||||
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
+
|
||||
+ /* Restore the saved value of the register SCFG_RCWPMUXCR0 */
|
||||
+ if (i2c_imx->need_set_pmuxcr == 1) {
|
||||
+ if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
|
||||
+ iowrite32be(pmuxcr, i2c_imx->pmuxcr_addr);
|
||||
+ else
|
||||
+ iowrite32(pmuxcr, i2c_imx->pmuxcr_addr);
|
||||
+ }
|
||||
+ /*
|
||||
+ * Set I2C_IBSR[IBAL] to clear the IBAL bit if-
|
||||
+ * I2C_IBSR[IBAL] = 1
|
||||
+ */
|
||||
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
+ if (temp & I2SR_IAL) {
|
||||
+ temp &= ~I2SR_IAL;
|
||||
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
@@ -888,6 +1019,19 @@ static int i2c_imx_xfer(struct i2c_adapt
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||
|
||||
+ /*
|
||||
+ * workround for ERR010027: ensure that the I2C BUS is idle
|
||||
+ * before switching to master mode and attempting a Start cycle
|
||||
+ */
|
||||
+ result = i2c_imx_bus_busy(i2c_imx, 0);
|
||||
+ if (result) {
|
||||
+ /* timeout */
|
||||
+ if ((result == -ETIMEDOUT) && (i2c_imx->layerscape_bus_recover == 1))
|
||||
+ i2c_imx_recovery_for_layerscape(i2c_imx);
|
||||
+ else
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
@@ -1030,6 +1174,50 @@ static int i2c_imx_init_recovery_info(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * switch SCL and SDA to their GPIO function and do some bitbanging
|
||||
+ * for bus recovery.
|
||||
+ * There are platforms such as Layerscape that don't support pinctrl, so add
|
||||
+ * workaround for layerscape, it has no effect for other platforms.
|
||||
+ */
|
||||
+static int i2c_imx_init_recovery_for_layerscape(
|
||||
+ struct imx_i2c_struct *i2c_imx,
|
||||
+ struct platform_device *pdev)
|
||||
+{
|
||||
+ const struct of_device_id *of_id;
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct pinmux_cfg *pinmux_cfg;
|
||||
+ struct device_node *scfg_node;
|
||||
+ void __iomem *scfg_base = NULL;
|
||||
+
|
||||
+ i2c_imx->gpio = of_get_named_gpio(np, "fsl-scl-gpio", 0);
|
||||
+ if (!gpio_is_valid(i2c_imx->gpio)) {
|
||||
+ dev_info(&pdev->dev, "fsl-scl-gpio not found\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ pinmux_cfg = devm_kzalloc(&pdev->dev, sizeof(*pinmux_cfg), GFP_KERNEL);
|
||||
+ if (!pinmux_cfg)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ i2c_imx->need_set_pmuxcr = 0;
|
||||
+ of_id = of_match_node(pinmux_of_match, np);
|
||||
+ if (of_id) {
|
||||
+ pinmux_cfg = (struct pinmux_cfg *)of_id->data;
|
||||
+ i2c_imx->pmuxcr_endian = pinmux_cfg->endian;
|
||||
+ i2c_imx->pmuxcr_set = pinmux_cfg->pmuxcr_set_bit;
|
||||
+ scfg_node = of_find_matching_node(NULL, scfg_device_ids);
|
||||
+ if (scfg_node) {
|
||||
+ scfg_base = of_iomap(scfg_node, 0);
|
||||
+ if (scfg_base) {
|
||||
+ i2c_imx->pmuxcr_addr = scfg_base + pinmux_cfg->pmuxcr_offset;
|
||||
+ i2c_imx->need_set_pmuxcr = 1;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ i2c_imx->layerscape_bus_recover = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static u32 i2c_imx_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||
@@ -1085,6 +1273,11 @@ static int i2c_imx_probe(struct platform
|
||||
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
|
||||
i2c_imx->base = base;
|
||||
|
||||
+ /* Init optional bus recovery for layerscape */
|
||||
+ ret = i2c_imx_init_recovery_for_layerscape(i2c_imx, pdev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* Get I2C clock */
|
||||
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c_imx->clk)) {
|
||||
@@ -1100,7 +1293,7 @@ static int i2c_imx_probe(struct platform
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
- ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
|
||||
+ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED,
|
||||
pdev->name, i2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
|
||||
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
|
||||
@@ -74,6 +74,7 @@ struct pca954x {
|
||||
u8 last_chan; /* last register value */
|
||||
u8 deselect;
|
||||
struct i2c_client *client;
|
||||
+ u8 disable_mux; /* do not disable mux if val not 0 */
|
||||
};
|
||||
|
||||
/* Provide specs for the PCA954x types we know about */
|
||||
@@ -196,6 +197,13 @@ static int pca954x_deselect_mux(struct i
|
||||
if (!(data->deselect & (1 << chan)))
|
||||
return 0;
|
||||
|
||||
+#ifdef CONFIG_ARCH_LAYERSCAPE
|
||||
+ if (data->disable_mux != 0)
|
||||
+ data->last_chan = data->chip->nchans;
|
||||
+ else
|
||||
+ data->last_chan = 0;
|
||||
+ return pca954x_reg_write(muxc->parent, client, data->disable_mux);
|
||||
+#endif
|
||||
/* Deselect active channel */
|
||||
data->last_chan = 0;
|
||||
return pca954x_reg_write(muxc->parent, client, data->last_chan);
|
||||
@@ -228,6 +236,28 @@ static int pca954x_probe(struct i2c_clie
|
||||
return -ENOMEM;
|
||||
data = i2c_mux_priv(muxc);
|
||||
|
||||
+#ifdef CONFIG_ARCH_LAYERSCAPE
|
||||
+ /* The point here is that you must not disable a mux if there
|
||||
+ * are no pullups on the input or you mess up the I2C. This
|
||||
+ * needs to be put into the DTS really as the kernel cannot
|
||||
+ * know this otherwise.
|
||||
+ */
|
||||
+ match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
|
||||
+ if (match)
|
||||
+ data->chip = of_device_get_match_data(&client->dev);
|
||||
+ else
|
||||
+ data->chip = &chips[id->driver_data];
|
||||
+
|
||||
+ data->disable_mux = of_node &&
|
||||
+ of_property_read_bool(of_node, "i2c-mux-never-disable") &&
|
||||
+ data->chip->muxtype == pca954x_ismux ?
|
||||
+ data->chip->enable : 0;
|
||||
+ /* force the first selection */
|
||||
+ if (data->disable_mux != 0)
|
||||
+ data->last_chan = data->chip->nchans;
|
||||
+ else
|
||||
+ data->last_chan = 0;
|
||||
+#endif
|
||||
i2c_set_clientdata(client, muxc);
|
||||
data->client = client;
|
||||
|
||||
@@ -240,11 +270,16 @@ static int pca954x_probe(struct i2c_clie
|
||||
* that the mux is in fact present. This also
|
||||
* initializes the mux to disconnected state.
|
||||
*/
|
||||
+#ifdef CONFIG_ARCH_LAYERSCAPE
|
||||
+ if (i2c_smbus_write_byte(client, data->disable_mux) < 0) {
|
||||
+#else
|
||||
if (i2c_smbus_write_byte(client, 0) < 0) {
|
||||
+#endif
|
||||
dev_warn(&client->dev, "probe failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
+#ifndef CONFIG_ARCH_LAYERSCAPE
|
||||
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
|
||||
if (match)
|
||||
data->chip = of_device_get_match_data(&client->dev);
|
||||
@@ -252,6 +287,7 @@ static int pca954x_probe(struct i2c_clie
|
||||
data->chip = &chips[id->driver_data];
|
||||
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
+#endif
|
||||
|
||||
idle_disconnect_dt = of_node &&
|
||||
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
||||
@@ -312,6 +348,13 @@ static int pca954x_resume(struct device
|
||||
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||
struct pca954x *data = i2c_mux_priv(muxc);
|
||||
|
||||
+#ifdef CONFIG_ARCH_LAYERSCAPE
|
||||
+ if (data->disable_mux != 0)
|
||||
+ data->last_chan = data->chip->nchans;
|
||||
+ else
|
||||
+ data->last_chan = 0;
|
||||
+ return i2c_smbus_write_byte(client, data->disable_mux);
|
||||
+#endif
|
||||
data->last_chan = 0;
|
||||
return i2c_smbus_write_byte(client, 0);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
From 5a5ff01c790d49c0f6fd247f68f2fd9a2128ea91 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:36:28 +0800
|
||||
Subject: [PATCH 23/30] irqchip: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape gic support.
|
||||
|
||||
Signed-off-by: Eric Auger <eric.auger@redhat.com>
|
||||
Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/irqchip/Makefile | 1 +
|
||||
drivers/irqchip/irq-gic-v3-its.c | 1 +
|
||||
include/linux/irqdomain.h | 36 ++++++++++++++++++++++++++++++++++++
|
||||
kernel/irq/irqdomain.c | 39 +++++++++++++++++++++++++++++++++++++++
|
||||
kernel/irq/msi.c | 4 ++--
|
||||
5 files changed, 79 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/irqchip/Makefile
|
||||
+++ b/drivers/irqchip/Makefile
|
||||
@@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scf
|
||||
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
|
||||
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
|
||||
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
|
||||
+obj-$(CONFIG_QUICC_ENGINE) += irq-qeic.o
|
||||
--- a/drivers/irqchip/irq-gic-v3-its.c
|
||||
+++ b/drivers/irqchip/irq-gic-v3-its.c
|
||||
@@ -1658,6 +1658,7 @@ static int its_init_domain(struct fwnode
|
||||
|
||||
inner_domain->parent = its_parent;
|
||||
inner_domain->bus_token = DOMAIN_BUS_NEXUS;
|
||||
+ inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_REMAP;
|
||||
info->ops = &its_msi_domain_ops;
|
||||
info->data = its;
|
||||
inner_domain->host_data = info;
|
||||
--- a/include/linux/irqdomain.h
|
||||
+++ b/include/linux/irqdomain.h
|
||||
@@ -183,6 +183,12 @@ enum {
|
||||
/* Irq domain is an IPI domain with single virq */
|
||||
IRQ_DOMAIN_FLAG_IPI_SINGLE = (1 << 3),
|
||||
|
||||
+ /* Irq domain implements MSIs */
|
||||
+ IRQ_DOMAIN_FLAG_MSI = (1 << 4),
|
||||
+
|
||||
+ /* Irq domain implements MSI remapping */
|
||||
+ IRQ_DOMAIN_FLAG_MSI_REMAP = (1 << 5),
|
||||
+
|
||||
/*
|
||||
* Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
|
||||
* for implementation specific purposes and ignored by the
|
||||
@@ -216,6 +222,7 @@ struct irq_domain *irq_domain_add_legacy
|
||||
void *host_data);
|
||||
extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
|
||||
enum irq_domain_bus_token bus_token);
|
||||
+extern bool irq_domain_check_msi_remap(void);
|
||||
extern void irq_set_default_host(struct irq_domain *host);
|
||||
extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
|
||||
irq_hw_number_t hwirq, int node,
|
||||
@@ -446,6 +453,19 @@ static inline bool irq_domain_is_ipi_sin
|
||||
{
|
||||
return domain->flags & IRQ_DOMAIN_FLAG_IPI_SINGLE;
|
||||
}
|
||||
+
|
||||
+static inline bool irq_domain_is_msi(struct irq_domain *domain)
|
||||
+{
|
||||
+ return domain->flags & IRQ_DOMAIN_FLAG_MSI;
|
||||
+}
|
||||
+
|
||||
+static inline bool irq_domain_is_msi_remap(struct irq_domain *domain)
|
||||
+{
|
||||
+ return domain->flags & IRQ_DOMAIN_FLAG_MSI_REMAP;
|
||||
+}
|
||||
+
|
||||
+extern bool irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain);
|
||||
+
|
||||
#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
static inline void irq_domain_activate_irq(struct irq_data *data) { }
|
||||
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
|
||||
@@ -477,6 +497,22 @@ static inline bool irq_domain_is_ipi_sin
|
||||
{
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+static inline bool irq_domain_is_msi(struct irq_domain *domain)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static inline bool irq_domain_is_msi_remap(struct irq_domain *domain)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static inline bool
|
||||
+irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
|
||||
#else /* CONFIG_IRQ_DOMAIN */
|
||||
--- a/kernel/irq/irqdomain.c
|
||||
+++ b/kernel/irq/irqdomain.c
|
||||
@@ -278,6 +278,31 @@ struct irq_domain *irq_find_matching_fws
|
||||
EXPORT_SYMBOL_GPL(irq_find_matching_fwspec);
|
||||
|
||||
/**
|
||||
+ * irq_domain_check_msi_remap - Check whether all MSI irq domains implement
|
||||
+ * IRQ remapping
|
||||
+ *
|
||||
+ * Return: false if any MSI irq domain does not support IRQ remapping,
|
||||
+ * true otherwise (including if there is no MSI irq domain)
|
||||
+ */
|
||||
+bool irq_domain_check_msi_remap(void)
|
||||
+{
|
||||
+ struct irq_domain *h;
|
||||
+ bool ret = true;
|
||||
+
|
||||
+ mutex_lock(&irq_domain_mutex);
|
||||
+ list_for_each_entry(h, &irq_domain_list, link) {
|
||||
+ if (irq_domain_is_msi(h) &&
|
||||
+ !irq_domain_hierarchical_is_msi_remap(h)) {
|
||||
+ ret = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ mutex_unlock(&irq_domain_mutex);
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(irq_domain_check_msi_remap);
|
||||
+
|
||||
+/**
|
||||
* irq_set_default_host() - Set a "default" irq domain
|
||||
* @domain: default domain pointer
|
||||
*
|
||||
@@ -1408,6 +1433,20 @@ static void irq_domain_check_hierarchy(s
|
||||
if (domain->ops->alloc)
|
||||
domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY;
|
||||
}
|
||||
+
|
||||
+/**
|
||||
+ * irq_domain_hierarchical_is_msi_remap - Check if the domain or any
|
||||
+ * parent has MSI remapping support
|
||||
+ * @domain: domain pointer
|
||||
+ */
|
||||
+bool irq_domain_hierarchical_is_msi_remap(struct irq_domain *domain)
|
||||
+{
|
||||
+ for (; domain; domain = domain->parent) {
|
||||
+ if (irq_domain_is_msi_remap(domain))
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
#else /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
/**
|
||||
* irq_domain_get_irq_data - Get irq_data associated with @virq and @domain
|
||||
--- a/kernel/irq/msi.c
|
||||
+++ b/kernel/irq/msi.c
|
||||
@@ -272,8 +272,8 @@ struct irq_domain *msi_create_irq_domain
|
||||
if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
|
||||
msi_domain_update_chip_ops(info);
|
||||
|
||||
- return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
|
||||
- &msi_domain_ops, info);
|
||||
+ return irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0,
|
||||
+ fwnode, &msi_domain_ops, info);
|
||||
}
|
||||
|
||||
int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
|
||||
@@ -0,0 +1,594 @@
|
||||
From 4215d5757595e7ec7ca146c2b901beb177f415d8 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:37:13 +0800
|
||||
Subject: [PATCH 24/30] mmc: layerscape support
|
||||
|
||||
This is an integrated patch for layerscape mmc support.
|
||||
|
||||
Adrian Hunter <adrian.hunter@intel.com>
|
||||
Jaehoon Chung <jh80.chung@samsung.com>
|
||||
Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/mmc/host/Kconfig | 1 +
|
||||
drivers/mmc/host/sdhci-esdhc.h | 52 +++++---
|
||||
drivers/mmc/host/sdhci-of-esdhc.c | 265 ++++++++++++++++++++++++++++++++++++--
|
||||
drivers/mmc/host/sdhci.c | 45 ++++---
|
||||
drivers/mmc/host/sdhci.h | 3 +
|
||||
5 files changed, 320 insertions(+), 46 deletions(-)
|
||||
|
||||
--- a/drivers/mmc/host/Kconfig
|
||||
+++ b/drivers/mmc/host/Kconfig
|
||||
@@ -144,6 +144,7 @@ config MMC_SDHCI_OF_ESDHC
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
+ select FSL_GUTS
|
||||
help
|
||||
This selects the Freescale eSDHC controller support.
|
||||
|
||||
--- a/drivers/mmc/host/sdhci-esdhc.h
|
||||
+++ b/drivers/mmc/host/sdhci-esdhc.h
|
||||
@@ -24,30 +24,46 @@
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
|
||||
SDHCI_QUIRK_NO_HISPD_BIT)
|
||||
|
||||
-#define ESDHC_PROCTL 0x28
|
||||
-
|
||||
-#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
-#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
-#define ESDHC_PREDIV_SHIFT 8
|
||||
-#define ESDHC_DIVIDER_SHIFT 4
|
||||
-#define ESDHC_CLOCK_PEREN 0x00000004
|
||||
-#define ESDHC_CLOCK_HCKEN 0x00000002
|
||||
-#define ESDHC_CLOCK_IPGEN 0x00000001
|
||||
-
|
||||
/* pltfm-specific */
|
||||
#define ESDHC_HOST_CONTROL_LE 0x20
|
||||
|
||||
/*
|
||||
- * P2020 interpretation of the SDHCI_HOST_CONTROL register
|
||||
+ * eSDHC register definition
|
||||
*/
|
||||
-#define ESDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
-#define ESDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
-#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
|
||||
-
|
||||
-/* OF-specific */
|
||||
-#define ESDHC_DMA_SYSCTL 0x40c
|
||||
-#define ESDHC_DMA_SNOOP 0x00000040
|
||||
|
||||
-#define ESDHC_HOST_CONTROL_RES 0x01
|
||||
+/* Present State Register */
|
||||
+#define ESDHC_PRSSTAT 0x24
|
||||
+#define ESDHC_CLOCK_STABLE 0x00000008
|
||||
+
|
||||
+/* Protocol Control Register */
|
||||
+#define ESDHC_PROCTL 0x28
|
||||
+#define ESDHC_VOLT_SEL 0x00000400
|
||||
+#define ESDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
+#define ESDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
+#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
|
||||
+#define ESDHC_HOST_CONTROL_RES 0x01
|
||||
+
|
||||
+/* System Control Register */
|
||||
+#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
+#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
+#define ESDHC_PREDIV_SHIFT 8
|
||||
+#define ESDHC_DIVIDER_SHIFT 4
|
||||
+#define ESDHC_CLOCK_SDCLKEN 0x00000008
|
||||
+#define ESDHC_CLOCK_PEREN 0x00000004
|
||||
+#define ESDHC_CLOCK_HCKEN 0x00000002
|
||||
+#define ESDHC_CLOCK_IPGEN 0x00000001
|
||||
+
|
||||
+/* Host Controller Capabilities Register 2 */
|
||||
+#define ESDHC_CAPABILITIES_1 0x114
|
||||
+
|
||||
+/* Tuning Block Control Register */
|
||||
+#define ESDHC_TBCTL 0x120
|
||||
+#define ESDHC_TB_EN 0x00000004
|
||||
+
|
||||
+/* Control Register for DMA transfer */
|
||||
+#define ESDHC_DMA_SYSCTL 0x40c
|
||||
+#define ESDHC_PERIPHERAL_CLK_SEL 0x00080000
|
||||
+#define ESDHC_FLUSH_ASYNC_FIFO 0x00040000
|
||||
+#define ESDHC_DMA_SNOOP 0x00000040
|
||||
|
||||
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
|
||||
--- a/drivers/mmc/host/sdhci-of-esdhc.c
|
||||
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
|
||||
@@ -16,8 +16,12 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/sys_soc.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/ktime.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "sdhci-esdhc.h"
|
||||
@@ -28,8 +32,12 @@
|
||||
struct sdhci_esdhc {
|
||||
u8 vendor_ver;
|
||||
u8 spec_ver;
|
||||
+ bool quirk_incorrect_hostver;
|
||||
+ unsigned int peripheral_clock;
|
||||
};
|
||||
|
||||
+static void esdhc_clock_enable(struct sdhci_host *host, bool enable);
|
||||
+
|
||||
/**
|
||||
* esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
|
||||
* to make it compatible with SD spec.
|
||||
@@ -80,6 +88,17 @@ static u32 esdhc_readl_fixup(struct sdhc
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * DTS properties of mmc host are used to enable each speed mode
|
||||
+ * according to soc and board capability. So clean up
|
||||
+ * SDR50/SDR104/DDR50 support bits here.
|
||||
+ */
|
||||
+ if (spec_reg == SDHCI_CAPABILITIES_1) {
|
||||
+ ret = value & (~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
|
||||
+ SDHCI_SUPPORT_DDR50));
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
ret = value;
|
||||
return ret;
|
||||
}
|
||||
@@ -87,6 +106,8 @@ static u32 esdhc_readl_fixup(struct sdhc
|
||||
static u16 esdhc_readw_fixup(struct sdhci_host *host,
|
||||
int spec_reg, u32 value)
|
||||
{
|
||||
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ret;
|
||||
int shift = (spec_reg & 0x2) * 8;
|
||||
|
||||
@@ -94,6 +115,12 @@ static u16 esdhc_readw_fixup(struct sdhc
|
||||
ret = value & 0xffff;
|
||||
else
|
||||
ret = (value >> shift) & 0xffff;
|
||||
+ /* Workaround for T4240-R1.0-R2.0 eSDHC which has incorrect
|
||||
+ * vendor version and spec version information.
|
||||
+ */
|
||||
+ if ((spec_reg == SDHCI_HOST_VERSION) &&
|
||||
+ (esdhc->quirk_incorrect_hostver))
|
||||
+ ret = (VENDOR_V_23 << SDHCI_VENDOR_VER_SHIFT) | SDHCI_SPEC_200;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -235,7 +262,11 @@ static u32 esdhc_be_readl(struct sdhci_h
|
||||
u32 ret;
|
||||
u32 value;
|
||||
|
||||
- value = ioread32be(host->ioaddr + reg);
|
||||
+ if (reg == SDHCI_CAPABILITIES_1)
|
||||
+ value = ioread32be(host->ioaddr + ESDHC_CAPABILITIES_1);
|
||||
+ else
|
||||
+ value = ioread32be(host->ioaddr + reg);
|
||||
+
|
||||
ret = esdhc_readl_fixup(host, reg, value);
|
||||
|
||||
return ret;
|
||||
@@ -246,7 +277,11 @@ static u32 esdhc_le_readl(struct sdhci_h
|
||||
u32 ret;
|
||||
u32 value;
|
||||
|
||||
- value = ioread32(host->ioaddr + reg);
|
||||
+ if (reg == SDHCI_CAPABILITIES_1)
|
||||
+ value = ioread32(host->ioaddr + ESDHC_CAPABILITIES_1);
|
||||
+ else
|
||||
+ value = ioread32(host->ioaddr + reg);
|
||||
+
|
||||
ret = esdhc_readl_fixup(host, reg, value);
|
||||
|
||||
return ret;
|
||||
@@ -404,15 +439,25 @@ static int esdhc_of_enable_dma(struct sd
|
||||
static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
- return pltfm_host->clock;
|
||||
+ if (esdhc->peripheral_clock)
|
||||
+ return esdhc->peripheral_clock;
|
||||
+ else
|
||||
+ return pltfm_host->clock;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
+ unsigned int clock;
|
||||
|
||||
- return pltfm_host->clock / 256 / 16;
|
||||
+ if (esdhc->peripheral_clock)
|
||||
+ clock = esdhc->peripheral_clock;
|
||||
+ else
|
||||
+ clock = pltfm_host->clock;
|
||||
+ return clock / 256 / 16;
|
||||
}
|
||||
|
||||
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
@@ -421,12 +466,15 @@ static void esdhc_of_set_clock(struct sd
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
int pre_div = 1;
|
||||
int div = 1;
|
||||
+ ktime_t timeout;
|
||||
u32 temp;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
- if (clock == 0)
|
||||
+ if (clock == 0) {
|
||||
+ esdhc_clock_enable(host, false);
|
||||
return;
|
||||
+ }
|
||||
|
||||
/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
|
||||
if (esdhc->vendor_ver < VENDOR_V_23)
|
||||
@@ -454,9 +502,15 @@ static void esdhc_of_set_clock(struct sd
|
||||
clock -= 5000000;
|
||||
}
|
||||
|
||||
+ /* Workaround to reduce the clock frequency for ls1021a esdhc */
|
||||
+ if (of_find_compatible_node(NULL, NULL, "fsl,ls1021a-esdhc")) {
|
||||
+ if (clock == 50000000)
|
||||
+ clock = 46500000;
|
||||
+ }
|
||||
+
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
- temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
|
||||
- | ESDHC_CLOCK_MASK);
|
||||
+ temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
|
||||
+ ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
|
||||
@@ -476,7 +530,20 @@ static void esdhc_of_set_clock(struct sd
|
||||
| (div << ESDHC_DIVIDER_SHIFT)
|
||||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
- mdelay(1);
|
||||
+
|
||||
+ /* Wait max 20 ms */
|
||||
+ timeout = ktime_add_ms(ktime_get(), 20);
|
||||
+ while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
|
||||
+ if (ktime_after(ktime_get(), timeout)) {
|
||||
+ pr_err("%s: Internal clock never stabilised.\n",
|
||||
+ mmc_hostname(host->mmc));
|
||||
+ return;
|
||||
+ }
|
||||
+ udelay(10);
|
||||
+ }
|
||||
+
|
||||
+ temp |= ESDHC_CLOCK_SDCLKEN;
|
||||
+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
}
|
||||
|
||||
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
@@ -501,12 +568,136 @@ static void esdhc_pltfm_set_bus_width(st
|
||||
sdhci_writel(host, ctrl, ESDHC_PROCTL);
|
||||
}
|
||||
|
||||
+static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
|
||||
+{
|
||||
+ u32 val;
|
||||
+ ktime_t timeout;
|
||||
+
|
||||
+ val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
+
|
||||
+ if (enable)
|
||||
+ val |= ESDHC_CLOCK_SDCLKEN;
|
||||
+ else
|
||||
+ val &= ~ESDHC_CLOCK_SDCLKEN;
|
||||
+
|
||||
+ sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL);
|
||||
+
|
||||
+ /* Wait max 20 ms */
|
||||
+ timeout = ktime_add_ms(ktime_get(), 20);
|
||||
+ val = ESDHC_CLOCK_STABLE;
|
||||
+ while (!(sdhci_readl(host, ESDHC_PRSSTAT) & val)) {
|
||||
+ if (ktime_after(ktime_get(), timeout)) {
|
||||
+ pr_err("%s: Internal clock never stabilised.\n",
|
||||
+ mmc_hostname(host->mmc));
|
||||
+ break;
|
||||
+ }
|
||||
+ udelay(10);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
+ u32 val;
|
||||
+
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
+
|
||||
+ if (mask & SDHCI_RESET_ALL) {
|
||||
+ val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
+ val &= ~ESDHC_TB_EN;
|
||||
+ sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* The SCFG, Supplemental Configuration Unit, provides SoC specific
|
||||
+ * configuration and status registers for the device. There is a
|
||||
+ * SDHC IO VSEL control register on SCFG for some platforms. It's
|
||||
+ * used to support SDHC IO voltage switching.
|
||||
+ */
|
||||
+static const struct of_device_id scfg_device_ids[] = {
|
||||
+ { .compatible = "fsl,t1040-scfg", },
|
||||
+ { .compatible = "fsl,ls1012a-scfg", },
|
||||
+ { .compatible = "fsl,ls1046a-scfg", },
|
||||
+ {}
|
||||
+};
|
||||
+
|
||||
+/* SDHC IO VSEL control register definition */
|
||||
+#define SCFG_SDHCIOVSELCR 0x408
|
||||
+#define SDHCIOVSELCR_TGLEN 0x80000000
|
||||
+#define SDHCIOVSELCR_VSELVAL 0x60000000
|
||||
+#define SDHCIOVSELCR_SDHC_VS 0x00000001
|
||||
+
|
||||
+static int esdhc_signal_voltage_switch(struct mmc_host *mmc,
|
||||
+ struct mmc_ios *ios)
|
||||
+{
|
||||
+ struct sdhci_host *host = mmc_priv(mmc);
|
||||
+ struct device_node *scfg_node;
|
||||
+ void __iomem *scfg_base = NULL;
|
||||
+ u32 sdhciovselcr;
|
||||
+ u32 val;
|
||||
+
|
||||
+ /*
|
||||
+ * Signal Voltage Switching is only applicable for Host Controllers
|
||||
+ * v3.00 and above.
|
||||
+ */
|
||||
+ if (host->version < SDHCI_SPEC_300)
|
||||
+ return 0;
|
||||
+
|
||||
+ val = sdhci_readl(host, ESDHC_PROCTL);
|
||||
+
|
||||
+ switch (ios->signal_voltage) {
|
||||
+ case MMC_SIGNAL_VOLTAGE_330:
|
||||
+ val &= ~ESDHC_VOLT_SEL;
|
||||
+ sdhci_writel(host, val, ESDHC_PROCTL);
|
||||
+ return 0;
|
||||
+ case MMC_SIGNAL_VOLTAGE_180:
|
||||
+ scfg_node = of_find_matching_node(NULL, scfg_device_ids);
|
||||
+ if (scfg_node)
|
||||
+ scfg_base = of_iomap(scfg_node, 0);
|
||||
+ if (scfg_base) {
|
||||
+ sdhciovselcr = SDHCIOVSELCR_TGLEN |
|
||||
+ SDHCIOVSELCR_VSELVAL;
|
||||
+ iowrite32be(sdhciovselcr,
|
||||
+ scfg_base + SCFG_SDHCIOVSELCR);
|
||||
+
|
||||
+ val |= ESDHC_VOLT_SEL;
|
||||
+ sdhci_writel(host, val, ESDHC_PROCTL);
|
||||
+ mdelay(5);
|
||||
+
|
||||
+ sdhciovselcr = SDHCIOVSELCR_TGLEN |
|
||||
+ SDHCIOVSELCR_SDHC_VS;
|
||||
+ iowrite32be(sdhciovselcr,
|
||||
+ scfg_base + SCFG_SDHCIOVSELCR);
|
||||
+ iounmap(scfg_base);
|
||||
+ } else {
|
||||
+ val |= ESDHC_VOLT_SEL;
|
||||
+ sdhci_writel(host, val, ESDHC_PROCTL);
|
||||
+ }
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
+{
|
||||
+ struct sdhci_host *host = mmc_priv(mmc);
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* Use tuning block for tuning procedure */
|
||||
+ esdhc_clock_enable(host, false);
|
||||
+ val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
||||
+ val |= ESDHC_FLUSH_ASYNC_FIFO;
|
||||
+ sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
|
||||
+
|
||||
+ val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
+ val |= ESDHC_TB_EN;
|
||||
+ sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
+ esdhc_clock_enable(host, true);
|
||||
+
|
||||
+ return sdhci_execute_tuning(mmc, opcode);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@@ -589,10 +780,19 @@ static const struct sdhci_pltfm_data sdh
|
||||
.ops = &sdhci_esdhc_le_ops,
|
||||
};
|
||||
|
||||
+static struct soc_device_attribute soc_incorrect_hostver[] = {
|
||||
+ { .family = "QorIQ T4240", .revision = "1.0", },
|
||||
+ { .family = "QorIQ T4240", .revision = "2.0", },
|
||||
+ { },
|
||||
+};
|
||||
+
|
||||
static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_esdhc *esdhc;
|
||||
+ struct device_node *np;
|
||||
+ struct clk *clk;
|
||||
+ u32 val;
|
||||
u16 host_ver;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
@@ -602,6 +802,36 @@ static void esdhc_init(struct platform_d
|
||||
esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
|
||||
SDHCI_VENDOR_VER_SHIFT;
|
||||
esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
|
||||
+ if (soc_device_match(soc_incorrect_hostver))
|
||||
+ esdhc->quirk_incorrect_hostver = true;
|
||||
+ else
|
||||
+ esdhc->quirk_incorrect_hostver = false;
|
||||
+
|
||||
+ np = pdev->dev.of_node;
|
||||
+ clk = of_clk_get(np, 0);
|
||||
+ if (!IS_ERR(clk)) {
|
||||
+ /*
|
||||
+ * esdhc->peripheral_clock would be assigned with a value
|
||||
+ * which is eSDHC base clock when use periperal clock.
|
||||
+ * For ls1046a, the clock value got by common clk API is
|
||||
+ * peripheral clock while the eSDHC base clock is 1/2
|
||||
+ * peripheral clock.
|
||||
+ */
|
||||
+ if (of_device_is_compatible(np, "fsl,ls1046a-esdhc"))
|
||||
+ esdhc->peripheral_clock = clk_get_rate(clk) / 2;
|
||||
+ else
|
||||
+ esdhc->peripheral_clock = clk_get_rate(clk);
|
||||
+
|
||||
+ clk_put(clk);
|
||||
+ }
|
||||
+
|
||||
+ if (esdhc->peripheral_clock) {
|
||||
+ esdhc_clock_enable(host, false);
|
||||
+ val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
||||
+ val |= ESDHC_PERIPHERAL_CLK_SEL;
|
||||
+ sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
|
||||
+ esdhc_clock_enable(host, true);
|
||||
+ }
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
@@ -624,6 +854,11 @@ static int sdhci_esdhc_probe(struct plat
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
+ host->mmc_host_ops.start_signal_voltage_switch =
|
||||
+ esdhc_signal_voltage_switch;
|
||||
+ host->mmc_host_ops.execute_tuning = esdhc_execute_tuning;
|
||||
+ host->tuning_delay = 1;
|
||||
+
|
||||
esdhc_init(pdev, host);
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
--- a/drivers/mmc/host/sdhci.c
|
||||
+++ b/drivers/mmc/host/sdhci.c
|
||||
@@ -1629,26 +1629,24 @@ static void sdhci_set_ios(struct mmc_hos
|
||||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
|
||||
- if ((ios->timing == MMC_TIMING_SD_HS ||
|
||||
- ios->timing == MMC_TIMING_MMC_HS)
|
||||
- && !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
|
||||
- ctrl |= SDHCI_CTRL_HISPD;
|
||||
- else
|
||||
- ctrl &= ~SDHCI_CTRL_HISPD;
|
||||
+ if (!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) {
|
||||
+ if ((ios->timing == MMC_TIMING_SD_HS ||
|
||||
+ ios->timing == MMC_TIMING_MMC_HS ||
|
||||
+ ios->timing == MMC_TIMING_MMC_HS400 ||
|
||||
+ ios->timing == MMC_TIMING_MMC_HS200 ||
|
||||
+ ios->timing == MMC_TIMING_MMC_DDR52 ||
|
||||
+ ios->timing == MMC_TIMING_UHS_SDR50 ||
|
||||
+ ios->timing == MMC_TIMING_UHS_SDR104 ||
|
||||
+ ios->timing == MMC_TIMING_UHS_DDR50 ||
|
||||
+ ios->timing == MMC_TIMING_UHS_SDR25))
|
||||
+ ctrl |= SDHCI_CTRL_HISPD;
|
||||
+ else
|
||||
+ ctrl &= ~SDHCI_CTRL_HISPD;
|
||||
+ }
|
||||
|
||||
if (host->version >= SDHCI_SPEC_300) {
|
||||
u16 clk, ctrl_2;
|
||||
|
||||
- /* In case of UHS-I modes, set High Speed Enable */
|
||||
- if ((ios->timing == MMC_TIMING_MMC_HS400) ||
|
||||
- (ios->timing == MMC_TIMING_MMC_HS200) ||
|
||||
- (ios->timing == MMC_TIMING_MMC_DDR52) ||
|
||||
- (ios->timing == MMC_TIMING_UHS_SDR50) ||
|
||||
- (ios->timing == MMC_TIMING_UHS_SDR104) ||
|
||||
- (ios->timing == MMC_TIMING_UHS_DDR50) ||
|
||||
- (ios->timing == MMC_TIMING_UHS_SDR25))
|
||||
- ctrl |= SDHCI_CTRL_HISPD;
|
||||
-
|
||||
if (!host->preset_enabled) {
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
/*
|
||||
@@ -1961,7 +1959,7 @@ static int sdhci_prepare_hs400_tuning(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
+int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
u16 ctrl;
|
||||
@@ -2020,6 +2018,9 @@ static int sdhci_execute_tuning(struct m
|
||||
return err;
|
||||
}
|
||||
|
||||
+ if (host->tuning_delay < 0)
|
||||
+ host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
|
||||
+
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
||||
if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND)
|
||||
@@ -2132,9 +2133,10 @@ static int sdhci_execute_tuning(struct m
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
|
||||
- /* eMMC spec does not require a delay between tuning cycles */
|
||||
- if (opcode == MMC_SEND_TUNING_BLOCK)
|
||||
- mdelay(1);
|
||||
+ /* Spec does not require a delay between tuning cycles */
|
||||
+ if (host->tuning_delay > 0)
|
||||
+ mdelay(host->tuning_delay);
|
||||
+
|
||||
} while (ctrl & SDHCI_CTRL_EXEC_TUNING);
|
||||
|
||||
/*
|
||||
@@ -2170,6 +2172,7 @@ out_unlock:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return err;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
|
||||
|
||||
static int sdhci_select_drive_strength(struct mmc_card *card,
|
||||
unsigned int max_dtr, int host_drv,
|
||||
@@ -2994,6 +2997,8 @@ struct sdhci_host *sdhci_alloc_host(stru
|
||||
|
||||
host->flags = SDHCI_SIGNALING_330;
|
||||
|
||||
+ host->tuning_delay = -1;
|
||||
+
|
||||
return host;
|
||||
}
|
||||
|
||||
--- a/drivers/mmc/host/sdhci.h
|
||||
+++ b/drivers/mmc/host/sdhci.h
|
||||
@@ -524,6 +524,8 @@ struct sdhci_host {
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
#define SDHCI_TUNING_MODE_2 1
|
||||
#define SDHCI_TUNING_MODE_3 2
|
||||
+ /* Delay (ms) between tuning commands */
|
||||
+ int tuning_delay;
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
@@ -689,6 +691,7 @@ void sdhci_set_power_noreg(struct sdhci_
|
||||
void sdhci_set_bus_width(struct sdhci_host *host, int width);
|
||||
void sdhci_reset(struct sdhci_host *host, u8 mask);
|
||||
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
|
||||
+int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int sdhci_suspend_host(struct sdhci_host *host);
|
||||
1976
target/linux/layerscape/patches-4.9/813-qe-support-layerscape.patch
Normal file
1976
target/linux/layerscape/patches-4.9/813-qe-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,682 @@
|
||||
From bda12381598c3df43f4e60362a8cd4af58b7f5b0 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:38:54 +0800
|
||||
Subject: [PATCH 26/30] rtc: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape rtc support.
|
||||
|
||||
Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/rtc/rtc-pcf85263.c | 665 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 665 insertions(+)
|
||||
create mode 100644 drivers/rtc/rtc-pcf85263.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/rtc/rtc-pcf85263.c
|
||||
@@ -0,0 +1,665 @@
|
||||
+/*
|
||||
+ * rtc-pcf85263 Driver for the NXP PCF85263 RTC
|
||||
+ * Copyright 2016 Parkeon
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/rtc.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/bcd.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+
|
||||
+#define DRV_NAME "rtc-pcf85263"
|
||||
+
|
||||
+/* Quartz capacitance */
|
||||
+#define PCF85263_QUARTZCAP_7pF 0
|
||||
+#define PCF85263_QUARTZCAP_6pF 1
|
||||
+#define PCF85263_QUARTZCAP_12p5pF 2
|
||||
+
|
||||
+/* Quartz drive strength */
|
||||
+#define PCF85263_QUARTZDRIVE_NORMAL 0
|
||||
+#define PCF85263_QUARTZDRIVE_LOW 1
|
||||
+#define PCF85263_QUARTZDRIVE_HIGH 2
|
||||
+
|
||||
+
|
||||
+#define PCF85263_REG_RTC_SC 0x01 /* Seconds */
|
||||
+#define PCF85263_REG_RTC_SC_OS BIT(7) /* Oscilator stopped flag */
|
||||
+
|
||||
+#define PCF85263_REG_RTC_MN 0x02 /* Minutes */
|
||||
+#define PCF85263_REG_RTC_HR 0x03 /* Hours */
|
||||
+#define PCF85263_REG_RTC_DT 0x04 /* Day of month 1-31 */
|
||||
+#define PCF85263_REG_RTC_DW 0x05 /* Day of week 0-6 */
|
||||
+#define PCF85263_REG_RTC_MO 0x06 /* Month 1-12 */
|
||||
+#define PCF85263_REG_RTC_YR 0x07 /* Year 0-99 */
|
||||
+
|
||||
+#define PCF85263_REG_ALM1_SC 0x08 /* Seconds */
|
||||
+#define PCF85263_REG_ALM1_MN 0x09 /* Minutes */
|
||||
+#define PCF85263_REG_ALM1_HR 0x0a /* Hours */
|
||||
+#define PCF85263_REG_ALM1_DT 0x0b /* Day of month 1-31 */
|
||||
+#define PCF85263_REG_ALM1_MO 0x0c /* Month 1-12 */
|
||||
+
|
||||
+#define PCF85263_REG_ALM_CTL 0x10
|
||||
+#define PCF85263_REG_ALM_CTL_ALL_A1E 0x1f /* sec,min,hr,day,mon alarm 1 */
|
||||
+
|
||||
+#define PCF85263_REG_OSC 0x25
|
||||
+#define PCF85263_REG_OSC_CL_MASK (BIT(0) | BIT(1))
|
||||
+#define PCF85263_REG_OSC_CL_SHIFT 0
|
||||
+#define PCF85263_REG_OSC_OSCD_MASK (BIT(2) | BIT(3))
|
||||
+#define PCF85263_REG_OSC_OSCD_SHIFT 2
|
||||
+#define PCF85263_REG_OSC_LOWJ BIT(4)
|
||||
+#define PCF85263_REG_OSC_12H BIT(5)
|
||||
+
|
||||
+#define PCF85263_REG_PINIO 0x27
|
||||
+#define PCF85263_REG_PINIO_INTAPM_MASK (BIT(0) | BIT(1))
|
||||
+#define PCF85263_REG_PINIO_INTAPM_SHIFT 0
|
||||
+#define PCF85263_INTAPM_INTA (0x2 << PCF85263_REG_PINIO_INTAPM_SHIFT)
|
||||
+#define PCF85263_INTAPM_HIGHZ (0x3 << PCF85263_REG_PINIO_INTAPM_SHIFT)
|
||||
+#define PCF85263_REG_PINIO_TSPM_MASK (BIT(2) | BIT(3))
|
||||
+#define PCF85263_REG_PINIO_TSPM_SHIFT 2
|
||||
+#define PCF85263_TSPM_DISABLED (0x0 << PCF85263_REG_PINIO_TSPM_SHIFT)
|
||||
+#define PCF85263_TSPM_INTB (0x1 << PCF85263_REG_PINIO_TSPM_SHIFT)
|
||||
+#define PCF85263_REG_PINIO_CLKDISABLE BIT(7)
|
||||
+
|
||||
+#define PCF85263_REG_FUNCTION 0x28
|
||||
+#define PCF85263_REG_FUNCTION_COF_MASK 0x7
|
||||
+#define PCF85263_REG_FUNCTION_COF_OFF 0x7 /* No clock output */
|
||||
+
|
||||
+#define PCF85263_REG_INTA_CTL 0x29
|
||||
+#define PCF85263_REG_INTB_CTL 0x2A
|
||||
+#define PCF85263_REG_INTx_CTL_A1E BIT(4) /* Alarm 1 */
|
||||
+#define PCF85263_REG_INTx_CTL_ILP BIT(7) /* 0=pulse, 1=level */
|
||||
+
|
||||
+#define PCF85263_REG_FLAGS 0x2B
|
||||
+#define PCF85263_REG_FLAGS_A1F BIT(5)
|
||||
+
|
||||
+#define PCF85263_REG_RAM_BYTE 0x2c
|
||||
+
|
||||
+#define PCF85263_REG_STOPENABLE 0x2e
|
||||
+#define PCF85263_REG_STOPENABLE_STOP BIT(0)
|
||||
+
|
||||
+#define PCF85263_REG_RESET 0x2f /* Reset command */
|
||||
+#define PCF85263_REG_RESET_CMD_CPR 0xa4 /* Clear prescaler */
|
||||
+
|
||||
+#define PCF85263_MAX_REG 0x2f
|
||||
+
|
||||
+#define PCF85263_HR_PM BIT(5)
|
||||
+
|
||||
+enum pcf85263_irqpin {
|
||||
+ PCF85263_IRQPIN_NONE,
|
||||
+ PCF85263_IRQPIN_INTA,
|
||||
+ PCF85263_IRQPIN_INTB
|
||||
+};
|
||||
+
|
||||
+static const char *const pcf85263_irqpin_names[] = {
|
||||
+ [PCF85263_IRQPIN_NONE] = "None",
|
||||
+ [PCF85263_IRQPIN_INTA] = "INTA",
|
||||
+ [PCF85263_IRQPIN_INTB] = "INTB"
|
||||
+};
|
||||
+
|
||||
+struct pcf85263 {
|
||||
+ struct device *dev;
|
||||
+ struct rtc_device *rtc;
|
||||
+ struct regmap *regmap;
|
||||
+ enum pcf85263_irqpin irq_pin;
|
||||
+ int irq;
|
||||
+ bool mode_12h;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Helpers to convert 12h to 24h and vice versa.
|
||||
+ * Values in register are stored in BCD with a PM flag in bit 5
|
||||
+ *
|
||||
+ * 23:00 <=> 11PM <=> 0x31
|
||||
+ * 00:00 <=> 12AM <=> 0x12
|
||||
+ * 01:00 <=> 1AM <=> 0x01
|
||||
+ * 12:00 <=> 12PM <=> 0x32
|
||||
+ * 13:00 <=> 1PM <=> 0x21
|
||||
+ */
|
||||
+static int pcf85263_bcd12h_to_bin24h(int regval)
|
||||
+{
|
||||
+ int hr = bcd2bin(regval & 0x1f);
|
||||
+ bool pm = regval & PCF85263_HR_PM;
|
||||
+
|
||||
+ if (hr == 12)
|
||||
+ return pm ? 12 : 0;
|
||||
+
|
||||
+ return pm ? hr + 12 : hr;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_bin24h_to_bcd12h(int hr24)
|
||||
+{
|
||||
+ bool pm = hr24 >= 12;
|
||||
+ int hr12 = hr24 % 12;
|
||||
+
|
||||
+ if (!hr12)
|
||||
+ hr12++;
|
||||
+
|
||||
+ return bin2bcd(hr12) | pm ? 0 : PCF85263_HR_PM;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_read_time(struct device *dev, struct rtc_time *tm)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+ const int first = PCF85263_REG_RTC_SC;
|
||||
+ const int last = PCF85263_REG_RTC_YR;
|
||||
+ const int len = last - first + 1;
|
||||
+ u8 regs[len];
|
||||
+ u8 hr_reg;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = regmap_bulk_read(pcf85263->regmap, first, regs, len);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (regs[PCF85263_REG_RTC_SC - first] & PCF85263_REG_RTC_SC_OS) {
|
||||
+ dev_warn(dev, "Oscillator stop detected, date/time is not reliable.\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ tm->tm_sec = bcd2bin(regs[PCF85263_REG_RTC_SC - first] & 0x7f);
|
||||
+ tm->tm_min = bcd2bin(regs[PCF85263_REG_RTC_MN - first] & 0x7f);
|
||||
+
|
||||
+ hr_reg = regs[PCF85263_REG_RTC_HR - first];
|
||||
+ if (pcf85263->mode_12h)
|
||||
+ tm->tm_hour = pcf85263_bcd12h_to_bin24h(hr_reg);
|
||||
+ else
|
||||
+ tm->tm_hour = bcd2bin(hr_reg & 0x3f);
|
||||
+
|
||||
+ tm->tm_mday = bcd2bin(regs[PCF85263_REG_RTC_DT - first]);
|
||||
+ tm->tm_wday = bcd2bin(regs[PCF85263_REG_RTC_DW - first]);
|
||||
+ tm->tm_mon = bcd2bin(regs[PCF85263_REG_RTC_MO - first]) - 1;
|
||||
+ tm->tm_year = bcd2bin(regs[PCF85263_REG_RTC_YR - first]);
|
||||
+
|
||||
+ tm->tm_year += 100; /* Assume 21st century */
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_set_time(struct device *dev, struct rtc_time *tm)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+
|
||||
+ /*
|
||||
+ * Before setting time need to stop RTC and disable prescaler
|
||||
+ * Do this all in a single I2C transaction exploiting wraparound
|
||||
+ * as described in data sheet.
|
||||
+ * This means that the array below must be in register order
|
||||
+ */
|
||||
+ u8 regs[] = {
|
||||
+ PCF85263_REG_STOPENABLE_STOP, /* STOP */
|
||||
+ PCF85263_REG_RESET_CMD_CPR, /* Disable prescaler */
|
||||
+ /* Wrap around to register 0 (1/100s) */
|
||||
+ 0, /* 1/100s always zero. */
|
||||
+ bin2bcd(tm->tm_sec),
|
||||
+ bin2bcd(tm->tm_min),
|
||||
+ bin2bcd(tm->tm_hour), /* 24-hour */
|
||||
+ bin2bcd(tm->tm_mday),
|
||||
+ bin2bcd(tm->tm_wday + 1),
|
||||
+ bin2bcd(tm->tm_mon + 1),
|
||||
+ bin2bcd(tm->tm_year % 100)
|
||||
+ };
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = regmap_bulk_write(pcf85263->regmap, PCF85263_REG_STOPENABLE,
|
||||
+ regs, sizeof(regs));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* As we have set the time in 24H update the hardware for that */
|
||||
+ if (pcf85263->mode_12h) {
|
||||
+ pcf85263->mode_12h = false;
|
||||
+ ret = regmap_update_bits(pcf85263->regmap, PCF85263_REG_OSC,
|
||||
+ PCF85263_REG_OSC_12H, 0);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Start it again */
|
||||
+ return regmap_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, 0);
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_enable_alarm(struct pcf85263 *pcf85263, bool enable)
|
||||
+{
|
||||
+ int reg;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = regmap_update_bits(pcf85263->regmap, PCF85263_REG_ALM_CTL,
|
||||
+ PCF85263_REG_ALM_CTL_ALL_A1E,
|
||||
+ enable ? PCF85263_REG_ALM_CTL_ALL_A1E : 0);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ switch (pcf85263->irq_pin) {
|
||||
+ case PCF85263_IRQPIN_NONE:
|
||||
+ return 0;
|
||||
+
|
||||
+ case PCF85263_IRQPIN_INTA:
|
||||
+ reg = PCF85263_REG_INTA_CTL;
|
||||
+ break;
|
||||
+
|
||||
+ case PCF85263_IRQPIN_INTB:
|
||||
+ reg = PCF85263_REG_INTB_CTL;
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return regmap_update_bits(pcf85263->regmap, reg,
|
||||
+ PCF85263_REG_INTx_CTL_A1E,
|
||||
+ enable ? PCF85263_REG_INTx_CTL_A1E : 0);
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+ struct rtc_time *tm = &alarm->time;
|
||||
+ const int first = PCF85263_REG_ALM1_SC;
|
||||
+ const int last = PCF85263_REG_ALM1_MO;
|
||||
+ const int len = last - first + 1;
|
||||
+ u8 regs[len];
|
||||
+ u8 hr_reg;
|
||||
+ unsigned int regval;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = regmap_bulk_read(pcf85263->regmap, first, regs, len);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ tm->tm_sec = bcd2bin(regs[PCF85263_REG_ALM1_SC - first] & 0x7f);
|
||||
+ tm->tm_min = bcd2bin(regs[PCF85263_REG_ALM1_MN - first] & 0x7f);
|
||||
+
|
||||
+ hr_reg = regs[PCF85263_REG_ALM1_HR - first];
|
||||
+ if (pcf85263->mode_12h)
|
||||
+ tm->tm_hour = pcf85263_bcd12h_to_bin24h(hr_reg);
|
||||
+ else
|
||||
+ tm->tm_hour = bcd2bin(hr_reg & 0x3f);
|
||||
+
|
||||
+ tm->tm_mday = bcd2bin(regs[PCF85263_REG_ALM1_DT - first]);
|
||||
+ tm->tm_mon = bcd2bin(regs[PCF85263_REG_ALM1_MO - first]) - 1;
|
||||
+ tm->tm_year = -1;
|
||||
+ tm->tm_wday = -1;
|
||||
+
|
||||
+ ret = regmap_read(pcf85263->regmap, PCF85263_REG_ALM_CTL, ®val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ alarm->enabled = !!(regval & PCF85263_REG_ALM_CTL_ALL_A1E);
|
||||
+
|
||||
+ ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, ®val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ alarm->pending = !!(regval & PCF85263_REG_FLAGS_A1F);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+ struct rtc_time *tm = &alarm->time;
|
||||
+ const int first = PCF85263_REG_ALM1_SC;
|
||||
+ const int last = PCF85263_REG_ALM1_MO;
|
||||
+ const int len = last - first + 1;
|
||||
+ u8 regs[len];
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Disable alarm comparison during update */
|
||||
+ ret = pcf85263_enable_alarm(pcf85263, false);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Clear any pending alarm (write 0=>clr, 1=>no change) */
|
||||
+ ret = regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS,
|
||||
+ (unsigned int)(~PCF85263_REG_FLAGS_A1F));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Set the alarm time registers */
|
||||
+ regs[PCF85263_REG_ALM1_SC - first] = bin2bcd(tm->tm_sec);
|
||||
+ regs[PCF85263_REG_ALM1_MN - first] = bin2bcd(tm->tm_min);
|
||||
+ regs[PCF85263_REG_ALM1_HR - first] = pcf85263->mode_12h ?
|
||||
+ pcf85263_bin24h_to_bcd12h(tm->tm_hour) :
|
||||
+ bin2bcd(tm->tm_hour);
|
||||
+ regs[PCF85263_REG_ALM1_DT - first] = bin2bcd(tm->tm_mday);
|
||||
+ regs[PCF85263_REG_ALM1_MO - first] = bin2bcd(tm->tm_mon + 1);
|
||||
+
|
||||
+ ret = regmap_bulk_write(pcf85263->regmap, first, regs, sizeof(regs));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (alarm->enabled)
|
||||
+ ret = pcf85263_enable_alarm(pcf85263, true);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+
|
||||
+ return pcf85263_enable_alarm(pcf85263, !!enable);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t pcf85263_irq(int irq, void *data)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = data;
|
||||
+ unsigned int regval;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, ®val);
|
||||
+ if (ret)
|
||||
+ return IRQ_NONE;
|
||||
+
|
||||
+ if (regval & PCF85263_REG_FLAGS_A1F) {
|
||||
+ regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS,
|
||||
+ (unsigned int)(~PCF85263_REG_FLAGS_A1F));
|
||||
+
|
||||
+ rtc_update_irq(pcf85263->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+ }
|
||||
+
|
||||
+ return IRQ_NONE;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_check_osc_stopped(struct pcf85263 *pcf85263)
|
||||
+{
|
||||
+ unsigned int regval;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = regmap_read(pcf85263->regmap, PCF85263_REG_RTC_SC, ®val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = regval & PCF85263_REG_RTC_SC_OS ? 1 : 0;
|
||||
+ if (ret)
|
||||
+ dev_warn(pcf85263->dev, "Oscillator stop detected, date/time is not reliable.\n");
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_RTC_INTF_DEV
|
||||
+static int pcf85263_ioctl(struct device *dev,
|
||||
+ unsigned int cmd, unsigned long arg)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+ int ret;
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case RTC_VL_READ:
|
||||
+ ret = pcf85263_check_osc_stopped(pcf85263);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (copy_to_user((void __user *)arg, &ret, sizeof(int)))
|
||||
+ return -EFAULT;
|
||||
+ return 0;
|
||||
+
|
||||
+ case RTC_VL_CLR:
|
||||
+ return regmap_update_bits(pcf85263->regmap,
|
||||
+ PCF85263_REG_RTC_SC,
|
||||
+ PCF85263_REG_RTC_SC_OS, 0);
|
||||
+ default:
|
||||
+ return -ENOIOCTLCMD;
|
||||
+ }
|
||||
+}
|
||||
+#else
|
||||
+#define pcf85263_ioctl NULL
|
||||
+#endif
|
||||
+
|
||||
+static int pcf85263_init_hw(struct pcf85263 *pcf85263)
|
||||
+{
|
||||
+ struct device_node *np = pcf85263->dev->of_node;
|
||||
+ unsigned int regval;
|
||||
+ u32 propval;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Determine if oscilator has been stopped (probably low power) */
|
||||
+ ret = pcf85263_check_osc_stopped(pcf85263);
|
||||
+ if (ret < 0) {
|
||||
+ /* Log here since this is the first hw access on probe */
|
||||
+ dev_err(pcf85263->dev, "Unable to read register\n");
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Determine 12/24H mode */
|
||||
+ ret = regmap_read(pcf85263->regmap, PCF85263_REG_OSC, ®val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ pcf85263->mode_12h = !!(regval & PCF85263_REG_OSC_12H);
|
||||
+
|
||||
+ /* Set oscilator register */
|
||||
+ regval &= ~PCF85263_REG_OSC_12H; /* keep current 12/24 h setting */
|
||||
+
|
||||
+ propval = PCF85263_QUARTZCAP_12p5pF;
|
||||
+ of_property_read_u32(np, "quartz-load-capacitance", &propval);
|
||||
+ regval |= ((propval << PCF85263_REG_OSC_CL_SHIFT)
|
||||
+ & PCF85263_REG_OSC_CL_MASK);
|
||||
+
|
||||
+ propval = PCF85263_QUARTZDRIVE_NORMAL;
|
||||
+ of_property_read_u32(np, "quartz-drive-strength", &propval);
|
||||
+ regval |= ((propval << PCF85263_REG_OSC_OSCD_SHIFT)
|
||||
+ & PCF85263_REG_OSC_OSCD_MASK);
|
||||
+
|
||||
+ if (of_property_read_bool(np, "quartz-low-jitter"))
|
||||
+ regval |= PCF85263_REG_OSC_LOWJ;
|
||||
+
|
||||
+ ret = regmap_write(pcf85263->regmap, PCF85263_REG_OSC, regval);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Set function register (RTC mode, 1s tick, clock output static) */
|
||||
+ ret = regmap_write(pcf85263->regmap, PCF85263_REG_FUNCTION,
|
||||
+ PCF85263_REG_FUNCTION_COF_OFF);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Set all interrupts to disabled, level mode */
|
||||
+ ret = regmap_write(pcf85263->regmap, PCF85263_REG_INTA_CTL,
|
||||
+ PCF85263_REG_INTx_CTL_ILP);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = regmap_write(pcf85263->regmap, PCF85263_REG_INTB_CTL,
|
||||
+ PCF85263_REG_INTx_CTL_ILP);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Setup IO pin config register */
|
||||
+ regval = PCF85263_REG_PINIO_CLKDISABLE;
|
||||
+ switch (pcf85263->irq_pin) {
|
||||
+ case PCF85263_IRQPIN_INTA:
|
||||
+ regval |= (PCF85263_INTAPM_INTA | PCF85263_TSPM_DISABLED);
|
||||
+ break;
|
||||
+ case PCF85263_IRQPIN_INTB:
|
||||
+ regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_INTB);
|
||||
+ break;
|
||||
+ case PCF85263_IRQPIN_NONE:
|
||||
+ regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_DISABLED);
|
||||
+ break;
|
||||
+ }
|
||||
+ ret = regmap_write(pcf85263->regmap, PCF85263_REG_PINIO, regval);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct rtc_class_ops rtc_ops = {
|
||||
+ .ioctl = pcf85263_ioctl,
|
||||
+ .read_time = pcf85263_read_time,
|
||||
+ .set_time = pcf85263_set_time,
|
||||
+ .read_alarm = pcf85263_read_alarm,
|
||||
+ .set_alarm = pcf85263_set_alarm,
|
||||
+ .alarm_irq_enable = pcf85263_alarm_irq_enable,
|
||||
+};
|
||||
+
|
||||
+static const struct regmap_config pcf85263_regmap_cfg = {
|
||||
+ .reg_bits = 8,
|
||||
+ .val_bits = 8,
|
||||
+ .max_register = PCF85263_MAX_REG,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * On some boards the interrupt line may not be wired to the CPU but only to
|
||||
+ * a power supply circuit.
|
||||
+ * In that case no interrupt will be specified in the device tree but the
|
||||
+ * wakeup-source DT property may be used to enable wakeup programming in
|
||||
+ * sysfs
|
||||
+ */
|
||||
+static bool pcf85263_can_wakeup_machine(struct pcf85263 *pcf85263)
|
||||
+{
|
||||
+ return pcf85263->irq ||
|
||||
+ of_property_read_bool(pcf85263->dev->of_node, "wakeup-source");
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_probe(struct i2c_client *client,
|
||||
+ const struct i2c_device_id *id)
|
||||
+{
|
||||
+ struct device *dev = &client->dev;
|
||||
+ struct pcf85263 *pcf85263;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
+ I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
+ I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ pcf85263 = devm_kzalloc(dev, sizeof(*pcf85263), GFP_KERNEL);
|
||||
+ if (!pcf85263)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ pcf85263->dev = dev;
|
||||
+ pcf85263->irq = client->irq;
|
||||
+ dev_set_drvdata(dev, pcf85263);
|
||||
+
|
||||
+ pcf85263->regmap = devm_regmap_init_i2c(client, &pcf85263_regmap_cfg);
|
||||
+ if (IS_ERR(pcf85263->regmap)) {
|
||||
+ ret = PTR_ERR(pcf85263->regmap);
|
||||
+ dev_err(dev, "regmap allocation failed (%d)\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Determine which interrupt pin the board uses */
|
||||
+ if (pcf85263_can_wakeup_machine(pcf85263)) {
|
||||
+ if (of_property_match_string(dev->of_node,
|
||||
+ "interrupt-names", "INTB") >= 0)
|
||||
+ pcf85263->irq_pin = PCF85263_IRQPIN_INTB;
|
||||
+ else
|
||||
+ pcf85263->irq_pin = PCF85263_IRQPIN_INTA;
|
||||
+ } else {
|
||||
+ pcf85263->irq_pin = PCF85263_IRQPIN_NONE;
|
||||
+ }
|
||||
+
|
||||
+ ret = pcf85263_init_hw(pcf85263);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (pcf85263->irq) {
|
||||
+ ret = devm_request_threaded_irq(dev, pcf85263->irq, NULL,
|
||||
+ pcf85263_irq,
|
||||
+ IRQF_ONESHOT,
|
||||
+ dev->driver->name, pcf85263);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "irq %d unavailable (%d)\n",
|
||||
+ pcf85263->irq, ret);
|
||||
+ pcf85263->irq = 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (pcf85263_can_wakeup_machine(pcf85263))
|
||||
+ device_init_wakeup(dev, true);
|
||||
+
|
||||
+ pcf85263->rtc = devm_rtc_device_register(dev, dev->driver->name,
|
||||
+ &rtc_ops, THIS_MODULE);
|
||||
+ ret = PTR_ERR_OR_ZERO(pcf85263->rtc);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We cannot support UIE mode if we do not have an IRQ line */
|
||||
+ if (!pcf85263->irq)
|
||||
+ pcf85263->rtc->uie_unsupported = 1;
|
||||
+
|
||||
+ dev_info(pcf85263->dev,
|
||||
+ "PCF85263 RTC (irqpin=%s irq=%d)\n",
|
||||
+ pcf85263_irqpin_names[pcf85263->irq_pin],
|
||||
+ pcf85263->irq);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_remove(struct i2c_client *client)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = i2c_get_clientdata(client);
|
||||
+
|
||||
+ if (pcf85263_can_wakeup_machine(pcf85263))
|
||||
+ device_init_wakeup(pcf85263->dev, false);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_PM_SLEEP
|
||||
+static int pcf85263_suspend(struct device *dev)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (device_may_wakeup(dev))
|
||||
+ ret = enable_irq_wake(pcf85263->irq);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int pcf85263_resume(struct device *dev)
|
||||
+{
|
||||
+ struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (device_may_wakeup(dev))
|
||||
+ ret = disable_irq_wake(pcf85263->irq);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+static const struct i2c_device_id pcf85263_id[] = {
|
||||
+ { "pcf85263", 0 },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(i2c, pcf85263_id);
|
||||
+
|
||||
+#ifdef CONFIG_OF
|
||||
+static const struct of_device_id pcf85263_of_match[] = {
|
||||
+ { .compatible = "nxp,pcf85263" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, pcf85263_of_match);
|
||||
+#endif
|
||||
+
|
||||
+static SIMPLE_DEV_PM_OPS(pcf85263_pm_ops, pcf85263_suspend, pcf85263_resume);
|
||||
+
|
||||
+static struct i2c_driver pcf85263_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "rtc-pcf85263",
|
||||
+ .of_match_table = of_match_ptr(pcf85263_of_match),
|
||||
+ .pm = &pcf85263_pm_ops,
|
||||
+ },
|
||||
+ .probe = pcf85263_probe,
|
||||
+ .remove = pcf85263_remove,
|
||||
+ .id_table = pcf85263_id,
|
||||
+};
|
||||
+
|
||||
+module_i2c_driver(pcf85263_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Martin Fuzzey <mfuzzey@parkeon.com>");
|
||||
+MODULE_DESCRIPTION("PCF85263 RTC Driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
@@ -0,0 +1,427 @@
|
||||
From 027b679f248f15dea36c6cd6782d6643e2151057 Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:39:43 +0800
|
||||
Subject: [PATCH 27/30] spi: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape dspi support.
|
||||
|
||||
Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
|
||||
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
|
||||
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
|
||||
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/spi/spi-fsl-dspi.c | 309 ++++++++++++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 304 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-fsl-dspi.c
|
||||
+++ b/drivers/spi/spi-fsl-dspi.c
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
+#include <linux/dmaengine.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -40,6 +42,7 @@
|
||||
#define TRAN_STATE_WORD_ODD_NUM 0x04
|
||||
|
||||
#define DSPI_FIFO_SIZE 4
|
||||
+#define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024)
|
||||
|
||||
#define SPI_MCR 0x00
|
||||
#define SPI_MCR_MASTER (1 << 31)
|
||||
@@ -72,6 +75,11 @@
|
||||
#define SPI_SR_TCFQF 0x80000000
|
||||
#define SPI_SR_CLEAR 0x9aaf0000
|
||||
|
||||
+#define SPI_RSER_TFFFE BIT(25)
|
||||
+#define SPI_RSER_TFFFD BIT(24)
|
||||
+#define SPI_RSER_RFDFE BIT(17)
|
||||
+#define SPI_RSER_RFDFD BIT(16)
|
||||
+
|
||||
#define SPI_RSER 0x30
|
||||
#define SPI_RSER_EOQFE 0x10000000
|
||||
#define SPI_RSER_TCFQE 0x80000000
|
||||
@@ -109,6 +117,8 @@
|
||||
|
||||
#define SPI_TCR_TCNT_MAX 0x10000
|
||||
|
||||
+#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000)
|
||||
+
|
||||
struct chip_data {
|
||||
u32 mcr_val;
|
||||
u32 ctar_val;
|
||||
@@ -118,6 +128,7 @@ struct chip_data {
|
||||
enum dspi_trans_mode {
|
||||
DSPI_EOQ_MODE = 0,
|
||||
DSPI_TCFQ_MODE,
|
||||
+ DSPI_DMA_MODE,
|
||||
};
|
||||
|
||||
struct fsl_dspi_devtype_data {
|
||||
@@ -126,7 +137,7 @@ struct fsl_dspi_devtype_data {
|
||||
};
|
||||
|
||||
static const struct fsl_dspi_devtype_data vf610_data = {
|
||||
- .trans_mode = DSPI_EOQ_MODE,
|
||||
+ .trans_mode = DSPI_DMA_MODE,
|
||||
.max_clock_factor = 2,
|
||||
};
|
||||
|
||||
@@ -140,6 +151,23 @@ static const struct fsl_dspi_devtype_dat
|
||||
.max_clock_factor = 8,
|
||||
};
|
||||
|
||||
+struct fsl_dspi_dma {
|
||||
+ /* Length of transfer in words of DSPI_FIFO_SIZE */
|
||||
+ u32 curr_xfer_len;
|
||||
+
|
||||
+ u32 *tx_dma_buf;
|
||||
+ struct dma_chan *chan_tx;
|
||||
+ dma_addr_t tx_dma_phys;
|
||||
+ struct completion cmd_tx_complete;
|
||||
+ struct dma_async_tx_descriptor *tx_desc;
|
||||
+
|
||||
+ u32 *rx_dma_buf;
|
||||
+ struct dma_chan *chan_rx;
|
||||
+ dma_addr_t rx_dma_phys;
|
||||
+ struct completion cmd_rx_complete;
|
||||
+ struct dma_async_tx_descriptor *rx_desc;
|
||||
+};
|
||||
+
|
||||
struct fsl_dspi {
|
||||
struct spi_master *master;
|
||||
struct platform_device *pdev;
|
||||
@@ -166,8 +194,11 @@ struct fsl_dspi {
|
||||
u32 waitflags;
|
||||
|
||||
u32 spi_tcnt;
|
||||
+ struct fsl_dspi_dma *dma;
|
||||
};
|
||||
|
||||
+static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word);
|
||||
+
|
||||
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
|
||||
{
|
||||
unsigned int val;
|
||||
@@ -177,6 +208,255 @@ static inline int is_double_byte_mode(st
|
||||
return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1;
|
||||
}
|
||||
|
||||
+static void dspi_tx_dma_callback(void *arg)
|
||||
+{
|
||||
+ struct fsl_dspi *dspi = arg;
|
||||
+ struct fsl_dspi_dma *dma = dspi->dma;
|
||||
+
|
||||
+ complete(&dma->cmd_tx_complete);
|
||||
+}
|
||||
+
|
||||
+static void dspi_rx_dma_callback(void *arg)
|
||||
+{
|
||||
+ struct fsl_dspi *dspi = arg;
|
||||
+ struct fsl_dspi_dma *dma = dspi->dma;
|
||||
+ int rx_word;
|
||||
+ int i;
|
||||
+ u16 d;
|
||||
+
|
||||
+ rx_word = is_double_byte_mode(dspi);
|
||||
+
|
||||
+ if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) {
|
||||
+ for (i = 0; i < dma->curr_xfer_len; i++) {
|
||||
+ d = dspi->dma->rx_dma_buf[i];
|
||||
+ rx_word ? (*(u16 *)dspi->rx = d) :
|
||||
+ (*(u8 *)dspi->rx = d);
|
||||
+ dspi->rx += rx_word + 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ complete(&dma->cmd_rx_complete);
|
||||
+}
|
||||
+
|
||||
+static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
|
||||
+{
|
||||
+ struct fsl_dspi_dma *dma = dspi->dma;
|
||||
+ struct device *dev = &dspi->pdev->dev;
|
||||
+ int time_left;
|
||||
+ int tx_word;
|
||||
+ int i;
|
||||
+
|
||||
+ tx_word = is_double_byte_mode(dspi);
|
||||
+
|
||||
+ for (i = 0; i < dma->curr_xfer_len; i++) {
|
||||
+ dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word);
|
||||
+ if ((dspi->cs_change) && (!dspi->len))
|
||||
+ dspi->dma->tx_dma_buf[i] &= ~SPI_PUSHR_CONT;
|
||||
+ }
|
||||
+
|
||||
+ dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx,
|
||||
+ dma->tx_dma_phys,
|
||||
+ dma->curr_xfer_len *
|
||||
+ DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
+ DMA_MEM_TO_DEV,
|
||||
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
+ if (!dma->tx_desc) {
|
||||
+ dev_err(dev, "Not able to get desc for DMA xfer\n");
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ dma->tx_desc->callback = dspi_tx_dma_callback;
|
||||
+ dma->tx_desc->callback_param = dspi;
|
||||
+ if (dma_submit_error(dmaengine_submit(dma->tx_desc))) {
|
||||
+ dev_err(dev, "DMA submit failed\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx,
|
||||
+ dma->rx_dma_phys,
|
||||
+ dma->curr_xfer_len *
|
||||
+ DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
+ DMA_DEV_TO_MEM,
|
||||
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
+ if (!dma->rx_desc) {
|
||||
+ dev_err(dev, "Not able to get desc for DMA xfer\n");
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ dma->rx_desc->callback = dspi_rx_dma_callback;
|
||||
+ dma->rx_desc->callback_param = dspi;
|
||||
+ if (dma_submit_error(dmaengine_submit(dma->rx_desc))) {
|
||||
+ dev_err(dev, "DMA submit failed\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ reinit_completion(&dspi->dma->cmd_rx_complete);
|
||||
+ reinit_completion(&dspi->dma->cmd_tx_complete);
|
||||
+
|
||||
+ dma_async_issue_pending(dma->chan_rx);
|
||||
+ dma_async_issue_pending(dma->chan_tx);
|
||||
+
|
||||
+ time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete,
|
||||
+ DMA_COMPLETION_TIMEOUT);
|
||||
+ if (time_left == 0) {
|
||||
+ dev_err(dev, "DMA tx timeout\n");
|
||||
+ dmaengine_terminate_all(dma->chan_tx);
|
||||
+ dmaengine_terminate_all(dma->chan_rx);
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ time_left = wait_for_completion_timeout(&dspi->dma->cmd_rx_complete,
|
||||
+ DMA_COMPLETION_TIMEOUT);
|
||||
+ if (time_left == 0) {
|
||||
+ dev_err(dev, "DMA rx timeout\n");
|
||||
+ dmaengine_terminate_all(dma->chan_tx);
|
||||
+ dmaengine_terminate_all(dma->chan_rx);
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int dspi_dma_xfer(struct fsl_dspi *dspi)
|
||||
+{
|
||||
+ struct fsl_dspi_dma *dma = dspi->dma;
|
||||
+ struct device *dev = &dspi->pdev->dev;
|
||||
+ int curr_remaining_bytes;
|
||||
+ int bytes_per_buffer;
|
||||
+ int word = 1;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (is_double_byte_mode(dspi))
|
||||
+ word = 2;
|
||||
+ curr_remaining_bytes = dspi->len;
|
||||
+ bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE;
|
||||
+ while (curr_remaining_bytes) {
|
||||
+ /* Check if current transfer fits the DMA buffer */
|
||||
+ dma->curr_xfer_len = curr_remaining_bytes / word;
|
||||
+ if (dma->curr_xfer_len > bytes_per_buffer)
|
||||
+ dma->curr_xfer_len = bytes_per_buffer;
|
||||
+
|
||||
+ ret = dspi_next_xfer_dma_submit(dspi);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "DMA transfer failed\n");
|
||||
+ goto exit;
|
||||
+
|
||||
+ } else {
|
||||
+ curr_remaining_bytes -= dma->curr_xfer_len * word;
|
||||
+ if (curr_remaining_bytes < 0)
|
||||
+ curr_remaining_bytes = 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+exit:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
|
||||
+{
|
||||
+ struct fsl_dspi_dma *dma;
|
||||
+ struct dma_slave_config cfg;
|
||||
+ struct device *dev = &dspi->pdev->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
||||
+ if (!dma)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ dma->chan_rx = dma_request_slave_channel(dev, "rx");
|
||||
+ if (!dma->chan_rx) {
|
||||
+ dev_err(dev, "rx dma channel not available\n");
|
||||
+ ret = -ENODEV;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ dma->chan_tx = dma_request_slave_channel(dev, "tx");
|
||||
+ if (!dma->chan_tx) {
|
||||
+ dev_err(dev, "tx dma channel not available\n");
|
||||
+ ret = -ENODEV;
|
||||
+ goto err_tx_channel;
|
||||
+ }
|
||||
+
|
||||
+ dma->tx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE,
|
||||
+ &dma->tx_dma_phys, GFP_KERNEL);
|
||||
+ if (!dma->tx_dma_buf) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_tx_dma_buf;
|
||||
+ }
|
||||
+
|
||||
+ dma->rx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE,
|
||||
+ &dma->rx_dma_phys, GFP_KERNEL);
|
||||
+ if (!dma->rx_dma_buf) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_rx_dma_buf;
|
||||
+ }
|
||||
+
|
||||
+ cfg.src_addr = phy_addr + SPI_POPR;
|
||||
+ cfg.dst_addr = phy_addr + SPI_PUSHR;
|
||||
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
+ cfg.src_maxburst = 1;
|
||||
+ cfg.dst_maxburst = 1;
|
||||
+
|
||||
+ cfg.direction = DMA_DEV_TO_MEM;
|
||||
+ ret = dmaengine_slave_config(dma->chan_rx, &cfg);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "can't configure rx dma channel\n");
|
||||
+ ret = -EINVAL;
|
||||
+ goto err_slave_config;
|
||||
+ }
|
||||
+
|
||||
+ cfg.direction = DMA_MEM_TO_DEV;
|
||||
+ ret = dmaengine_slave_config(dma->chan_tx, &cfg);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "can't configure tx dma channel\n");
|
||||
+ ret = -EINVAL;
|
||||
+ goto err_slave_config;
|
||||
+ }
|
||||
+
|
||||
+ dspi->dma = dma;
|
||||
+ init_completion(&dma->cmd_tx_complete);
|
||||
+ init_completion(&dma->cmd_rx_complete);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_slave_config:
|
||||
+ dma_free_coherent(dev, DSPI_DMA_BUFSIZE,
|
||||
+ dma->rx_dma_buf, dma->rx_dma_phys);
|
||||
+err_rx_dma_buf:
|
||||
+ dma_free_coherent(dev, DSPI_DMA_BUFSIZE,
|
||||
+ dma->tx_dma_buf, dma->tx_dma_phys);
|
||||
+err_tx_dma_buf:
|
||||
+ dma_release_channel(dma->chan_tx);
|
||||
+err_tx_channel:
|
||||
+ dma_release_channel(dma->chan_rx);
|
||||
+
|
||||
+ devm_kfree(dev, dma);
|
||||
+ dspi->dma = NULL;
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void dspi_release_dma(struct fsl_dspi *dspi)
|
||||
+{
|
||||
+ struct fsl_dspi_dma *dma = dspi->dma;
|
||||
+ struct device *dev = &dspi->pdev->dev;
|
||||
+
|
||||
+ if (dma) {
|
||||
+ if (dma->chan_tx) {
|
||||
+ dma_unmap_single(dev, dma->tx_dma_phys,
|
||||
+ DSPI_DMA_BUFSIZE, DMA_TO_DEVICE);
|
||||
+ dma_release_channel(dma->chan_tx);
|
||||
+ }
|
||||
+
|
||||
+ if (dma->chan_rx) {
|
||||
+ dma_unmap_single(dev, dma->rx_dma_phys,
|
||||
+ DSPI_DMA_BUFSIZE, DMA_FROM_DEVICE);
|
||||
+ dma_release_channel(dma->chan_rx);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
|
||||
unsigned long clkrate)
|
||||
{
|
||||
@@ -425,6 +705,12 @@ static int dspi_transfer_one_message(str
|
||||
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
|
||||
dspi_tcfq_write(dspi);
|
||||
break;
|
||||
+ case DSPI_DMA_MODE:
|
||||
+ regmap_write(dspi->regmap, SPI_RSER,
|
||||
+ SPI_RSER_TFFFE | SPI_RSER_TFFFD |
|
||||
+ SPI_RSER_RFDFE | SPI_RSER_RFDFD);
|
||||
+ status = dspi_dma_xfer(dspi);
|
||||
+ break;
|
||||
default:
|
||||
dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
|
||||
trans_mode);
|
||||
@@ -432,9 +718,13 @@ static int dspi_transfer_one_message(str
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
|
||||
- dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
|
||||
- dspi->waitflags = 0;
|
||||
+ if (trans_mode != DSPI_DMA_MODE) {
|
||||
+ if (wait_event_interruptible(dspi->waitq,
|
||||
+ dspi->waitflags))
|
||||
+ dev_err(&dspi->pdev->dev,
|
||||
+ "wait transfer complete fail!\n");
|
||||
+ dspi->waitflags = 0;
|
||||
+ }
|
||||
|
||||
if (transfer->delay_usecs)
|
||||
udelay(transfer->delay_usecs);
|
||||
@@ -712,7 +1002,8 @@ static int dspi_probe(struct platform_de
|
||||
if (IS_ERR(dspi->regmap)) {
|
||||
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
|
||||
PTR_ERR(dspi->regmap));
|
||||
- return PTR_ERR(dspi->regmap);
|
||||
+ ret = PTR_ERR(dspi->regmap);
|
||||
+ goto out_master_put;
|
||||
}
|
||||
|
||||
dspi->clk = devm_clk_get(&pdev->dev, "dspi");
|
||||
@@ -740,6 +1031,13 @@ static int dspi_probe(struct platform_de
|
||||
goto out_clk_put;
|
||||
}
|
||||
|
||||
+ if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) {
|
||||
+ if (dspi_request_dma(dspi, res->start)) {
|
||||
+ dev_err(&pdev->dev, "can't get dma channels\n");
|
||||
+ goto out_clk_put;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
master->max_speed_hz =
|
||||
clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor;
|
||||
|
||||
@@ -768,6 +1066,7 @@ static int dspi_remove(struct platform_d
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(master);
|
||||
|
||||
/* Disconnect from the SPI framework */
|
||||
+ dspi_release_dma(dspi);
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
spi_unregister_master(dspi->master);
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
From c35aec61e5bb0faafb2847a0d750ebd7345a4b0f Mon Sep 17 00:00:00 2001
|
||||
From: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
Date: Wed, 17 Jan 2018 15:40:24 +0800
|
||||
Subject: [PATCH 28/30] tty: serial: support layerscape
|
||||
|
||||
This is an integrated patch for layerscape uart support.
|
||||
|
||||
Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
|
||||
Signed-off-by: Yuan Yao <yao.yuan@nxp.com>
|
||||
Signed-off-by: Stefan Agner <stefan@agner.ch>
|
||||
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
---
|
||||
drivers/tty/serial/fsl_lpuart.c | 66 ++++++++++++++++++++++++++++-------------
|
||||
1 file changed, 46 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/tty/serial/fsl_lpuart.c
|
||||
+++ b/drivers/tty/serial/fsl_lpuart.c
|
||||
@@ -231,6 +231,8 @@
|
||||
#define DEV_NAME "ttyLP"
|
||||
#define UART_NR 6
|
||||
|
||||
+static DECLARE_BITMAP(linemap, UART_NR);
|
||||
+
|
||||
struct lpuart_port {
|
||||
struct uart_port port;
|
||||
struct clk *clk;
|
||||
@@ -1351,6 +1353,18 @@ lpuart_set_termios(struct uart_port *por
|
||||
/* ask the core to calculate the divisor */
|
||||
baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
|
||||
|
||||
+ /*
|
||||
+ * Need to update the Ring buffer length according to the selected
|
||||
+ * baud rate and restart Rx DMA path.
|
||||
+ *
|
||||
+ * Since timer function acqures sport->port.lock, need to stop before
|
||||
+ * acquring same lock because otherwise del_timer_sync() can deadlock.
|
||||
+ */
|
||||
+ if (old && sport->lpuart_dma_rx_use) {
|
||||
+ del_timer_sync(&sport->lpuart_timer);
|
||||
+ lpuart_dma_rx_free(&sport->port);
|
||||
+ }
|
||||
+
|
||||
spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
sport->port.read_status_mask = 0;
|
||||
@@ -1400,22 +1414,11 @@ lpuart_set_termios(struct uart_port *por
|
||||
/* restore control register */
|
||||
writeb(old_cr2, sport->port.membase + UARTCR2);
|
||||
|
||||
- /*
|
||||
- * If new baud rate is set, we will also need to update the Ring buffer
|
||||
- * length according to the selected baud rate and restart Rx DMA path.
|
||||
- */
|
||||
- if (old) {
|
||||
- if (sport->lpuart_dma_rx_use) {
|
||||
- del_timer_sync(&sport->lpuart_timer);
|
||||
- lpuart_dma_rx_free(&sport->port);
|
||||
- }
|
||||
-
|
||||
- if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
|
||||
- sport->lpuart_dma_rx_use = true;
|
||||
+ if (old && sport->lpuart_dma_rx_use) {
|
||||
+ if (!lpuart_start_rx_dma(sport))
|
||||
rx_dma_timer_init(sport);
|
||||
- } else {
|
||||
+ else
|
||||
sport->lpuart_dma_rx_use = false;
|
||||
- }
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
@@ -1645,6 +1648,13 @@ lpuart_console_write(struct console *co,
|
||||
{
|
||||
struct lpuart_port *sport = lpuart_ports[co->index];
|
||||
unsigned char old_cr2, cr2;
|
||||
+ unsigned long flags;
|
||||
+ int locked = 1;
|
||||
+
|
||||
+ if (sport->port.sysrq || oops_in_progress)
|
||||
+ locked = spin_trylock_irqsave(&sport->port.lock, flags);
|
||||
+ else
|
||||
+ spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
/* first save CR2 and then disable interrupts */
|
||||
cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
|
||||
@@ -1659,6 +1669,9 @@ lpuart_console_write(struct console *co,
|
||||
barrier();
|
||||
|
||||
writeb(old_cr2, sport->port.membase + UARTCR2);
|
||||
+
|
||||
+ if (locked)
|
||||
+ spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1666,6 +1679,13 @@ lpuart32_console_write(struct console *c
|
||||
{
|
||||
struct lpuart_port *sport = lpuart_ports[co->index];
|
||||
unsigned long old_cr, cr;
|
||||
+ unsigned long flags;
|
||||
+ int locked = 1;
|
||||
+
|
||||
+ if (sport->port.sysrq || oops_in_progress)
|
||||
+ locked = spin_trylock_irqsave(&sport->port.lock, flags);
|
||||
+ else
|
||||
+ spin_lock_irqsave(&sport->port.lock, flags);
|
||||
|
||||
/* first save CR2 and then disable interrupts */
|
||||
cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL);
|
||||
@@ -1680,6 +1700,9 @@ lpuart32_console_write(struct console *c
|
||||
barrier();
|
||||
|
||||
lpuart32_write(old_cr, sport->port.membase + UARTCTRL);
|
||||
+
|
||||
+ if (locked)
|
||||
+ spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1904,13 +1927,13 @@ static int lpuart_probe(struct platform_
|
||||
|
||||
ret = of_alias_get_id(np, "serial");
|
||||
if (ret < 0) {
|
||||
- dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
|
||||
- return ret;
|
||||
- }
|
||||
- if (ret >= ARRAY_SIZE(lpuart_ports)) {
|
||||
- dev_err(&pdev->dev, "serial%d out of range\n", ret);
|
||||
- return -EINVAL;
|
||||
+ ret = find_first_zero_bit(linemap, UART_NR);
|
||||
+ if (ret >= UART_NR) {
|
||||
+ dev_err(&pdev->dev, "port line is full, add device failed\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
}
|
||||
+ set_bit(ret, linemap);
|
||||
sport->port.line = ret;
|
||||
sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart");
|
||||
|
||||
@@ -1992,6 +2015,7 @@ static int lpuart_remove(struct platform
|
||||
struct lpuart_port *sport = platform_get_drvdata(pdev);
|
||||
|
||||
uart_remove_one_port(&lpuart_reg, &sport->port);
|
||||
+ clear_bit(sport->port.line, linemap);
|
||||
|
||||
clk_disable_unprepare(sport->clk);
|
||||
|
||||
@@ -2076,12 +2100,10 @@ static int lpuart_resume(struct device *
|
||||
|
||||
if (sport->lpuart_dma_rx_use) {
|
||||
if (sport->port.irq_wake) {
|
||||
- if (!lpuart_start_rx_dma(sport)) {
|
||||
- sport->lpuart_dma_rx_use = true;
|
||||
+ if (!lpuart_start_rx_dma(sport))
|
||||
rx_dma_timer_init(sport);
|
||||
- } else {
|
||||
+ else
|
||||
sport->lpuart_dma_rx_use = false;
|
||||
- }
|
||||
}
|
||||
}
|
||||
|
||||
1710
target/linux/layerscape/patches-4.9/817-usb-support-layerscape.patch
Normal file
1710
target/linux/layerscape/patches-4.9/817-usb-support-layerscape.patch
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user