The i.MX SoC changes for 4.3:

- Add i.MX6 Ultralite SoC support, which is the newest addition to
    i.MX6 family.  It integrates a single Cortex-A7 core and a power
    management module that reduces the complexity of external power
    supply and simplifies power sequencing.
  - Change SNVS RTC driver to use syscon interface for register access,
    and add SNVS power key driver support.
  - Add a second clock for mxc rtc driver, and support device tree probe
    for the driver.
  - Add FEC MAC reference clock and phy fixup initialization for i.MX6UL
    platform.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJVyg8nAAoJEFBXWFqHsHzOQC8H/iY+cdNfAWIxYmt2CeF607su
 fLaycUSUPqPAERUTcHpjKyiKkRg2NWV7vFVWCkKaQ3RZ+IW6xNntkqMxzocS1sh2
 +70Ckp+B0orGuo96PkEXua9fNPf8/yaGiDhuJpK966VRRSSXRD15uOuqHAJ2Jz/v
 HEnEm3KANSSYS1heEJRqiiCsqhADRWl2RzgfV327aXtScP9zXlbJGlEc/jUVAY65
 wbqjsXdySeS3rECNMAYXnPU7IlK4NkRqrOi1JmTJCBlXqV2b6dBfjgIu9jOa91UG
 yRj7IEBJemqT4Ap1ee2NR3H1lDngt2JKg9XqRDL3j9alYvhAWGvhSY2UH2iPrFg=
 =emep
 -----END PGP SIGNATURE-----

Merge tag 'imx-soc-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into next/soc

The i.MX SoC changes for 4.3:
 - Add i.MX6 Ultralite SoC support, which is the newest addition to
   i.MX6 family.  It integrates a single Cortex-A7 core and a power
   management module that reduces the complexity of external power
   supply and simplifies power sequencing.
 - Change SNVS RTC driver to use syscon interface for register access,
   and add SNVS power key driver support.
 - Add a second clock for mxc rtc driver, and support device tree probe
   for the driver.
 - Add FEC MAC reference clock and phy fixup initialization for i.MX6UL
   platform.

* tag 'imx-soc-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  rtc: snvs: select option REGMAP_MMIO
  ARM: imx6ul: add fec MAC refrence clock and phy fixup init
  ARM: imx6ul: add fec bits to GPR syscon definition
  rtc: mxc: add support of device tree
  dt-binding: document the binding for mxc rtc
  rtc: mxc: use a second rtc clock
  input: snvs_pwrkey: use "wakeup-source" as deivce tree property name
  Document: devicetree: input: imx: i.mx snvs power device tree bindings
  input: keyboard: imx: add snvs power key driver
  Document: dt: fsl: snvs: change support syscon
  rtc: snvs: use syscon to access register
  ARM: imx: add low-level debug support for i.mx6ul
  ARM: imx: add i.mx6ul msl support

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2015-08-18 13:10:05 -07:00
commit 207b504a63
17 changed files with 592 additions and 92 deletions

View File

@ -288,12 +288,13 @@ Secure Non-Volatile Storage (SNVS) Node
Node defines address range and the associated
interrupt for the SNVS function. This function
monitors security state information & reports
security violations.
security violations. This also included rtc,
system power off and ON/OFF key.
- compatible
Usage: required
Value type: <string>
Definition: Must include "fsl,sec-v4.0-mon".
Definition: Must include "fsl,sec-v4.0-mon" and "syscon".
- reg
Usage: required
@ -324,7 +325,7 @@ Secure Non-Volatile Storage (SNVS) Node
the child address, parent address, & length.
- interrupts
Usage: required
Usage: optional
Value type: <prop_encoded-array>
Definition: Specifies the interrupts generated by this
device. The value of the interrupts property
@ -341,7 +342,7 @@ Secure Non-Volatile Storage (SNVS) Node
EXAMPLE
sec_mon@314000 {
compatible = "fsl,sec-v4.0-mon";
compatible = "fsl,sec-v4.0-mon", "syscon";
reg = <0x314000 0x1000>;
ranges = <0 0x314000 0x1000>;
interrupt-parent = <&mpic>;
@ -358,16 +359,72 @@ Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
Value type: <string>
Definition: Must include "fsl,sec-v4.0-mon-rtc-lp".
- reg
- interrupts
Usage: required
Value type: <prop-encoded-array>
Definition: A standard property. Specifies the physical
address and length of the SNVS LP configuration registers.
Value type: <prop_encoded-array>
Definition: Specifies the interrupts generated by this
device. The value of the interrupts property
consists of one interrupt specifier. The format
of the specifier is defined by the binding document
describing the node's interrupt parent.
- regmap
Usage: required
Value type: <phandle>
Definition: this is phandle to the register map node.
- offset
Usage: option
value type: <u32>
Definition: LP register offset. default it is 0x34.
EXAMPLE
sec_mon_rtc_lp@314000 {
sec_mon_rtc_lp@1 {
compatible = "fsl,sec-v4.0-mon-rtc-lp";
reg = <0x34 0x58>;
interrupts = <93 2>;
regmap = <&snvs>;
offset = <0x34>;
};
=====================================================================
System ON/OFF key driver
The snvs-pwrkey is designed to enable POWER key function which controlled
by SNVS ONOFF, the driver can report the status of POWER key and wakeup
system if pressed after system suspend.
- compatible:
Usage: required
Value type: <string>
Definition: Mush include "fsl,sec-v4.0-pwrkey".
- interrupts:
Usage: required
Value type: <prop_encoded-array>
Definition: The SNVS ON/OFF interrupt number to the CPU(s).
- linux,keycode:
Usage: option
Value type: <int>
Definition: Keycode to emit, KEY_POWER by default.
- wakeup-source:
Usage: option
Value type: <boo>
Definition: Button can wake-up the system.
- regmap:
Usage: required:
Value type: <phandle>
Definition: this is phandle to the register map node.
EXAMPLE:
snvs-pwrkey@0x020cc000 {
compatible = "fsl,sec-v4.0-pwrkey";
regmap = <&snvs>;
interrupts = <0 4 0x4>
linux,keycode = <116>; /* KEY_POWER */
wakeup;
};
=====================================================================
@ -443,12 +500,20 @@ FULL EXAMPLE
compatible = "fsl,sec-v4.0-mon";
reg = <0x314000 0x1000>;
ranges = <0 0x314000 0x1000>;
interrupt-parent = <&mpic>;
interrupts = <93 2>;
sec_mon_rtc_lp@34 {
compatible = "fsl,sec-v4.0-mon-rtc-lp";
reg = <0x34 0x58>;
regmap = <&sec_mon>;
offset = <0x34>;
interrupts = <93 2>;
};
snvs-pwrkey@0x020cc000 {
compatible = "fsl,sec-v4.0-pwrkey";
regmap = <&sec_mon>;
interrupts = <0 4 0x4>;
linux,keycode = <116>; /* KEY_POWER */
wakeup;
};
};

View File

@ -0,0 +1 @@
See Documentation/devicetree/bindings/crypto/fsl-sec4.txt

View File

@ -0,0 +1,26 @@
* Real Time Clock of the i.MX SoCs
RTC controller for the i.MX SoCs
Required properties:
- compatible: Should be "fsl,imx1-rtc" or "fsl,imx21-rtc".
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: IRQ line for the RTC.
- clocks: should contain two entries:
* one for the input reference
* one for the the SoC RTC
- clock-names: should contain:
* "ref" for the input reference clock
* "ipg" for the SoC RTC clock
Example:
rtc@10007000 {
compatible = "fsl,imx21-rtc";
reg = <0x10007000 0x1000>;
interrupts = <22>;
clocks = <&clks IMX27_CLK_CKIL>,
<&clks IMX27_CLK_RTC_IPG_GATE>;
clock-names = "ref", "ipg";
};

View File

@ -417,6 +417,13 @@ choice
Say Y here if you want kernel low-level debugging support
on i.MX6SX.
config DEBUG_IMX6UL_UART
bool "i.MX6UL Debug UART"
depends on SOC_IMX6UL
help
Say Y here if you want kernel low-level debugging support
on i.MX6UL.
config DEBUG_IMX7D_UART
bool "i.MX7D Debug UART"
depends on SOC_IMX7D
@ -1275,6 +1282,7 @@ config DEBUG_IMX_UART_PORT
DEBUG_IMX6Q_UART || \
DEBUG_IMX6SL_UART || \
DEBUG_IMX6SX_UART || \
DEBUG_IMX6UL_UART || \
DEBUG_IMX7D_UART
default 1
depends on ARCH_MXC
@ -1326,6 +1334,7 @@ config DEBUG_LL_INCLUDE
DEBUG_IMX6Q_UART || \
DEBUG_IMX6SL_UART || \
DEBUG_IMX6SX_UART || \
DEBUG_IMX6UL_UART || \
DEBUG_IMX7D_UART
default "debug/ks8695.S" if DEBUG_KS8695_UART
default "debug/msm.S" if DEBUG_QCOM_UARTDM

View File

@ -90,6 +90,17 @@
#define IMX6SX_UART_BASE_ADDR(n) IMX6SX_UART##n##_BASE_ADDR
#define IMX6SX_UART_BASE(n) IMX6SX_UART_BASE_ADDR(n)
#define IMX6UL_UART1_BASE_ADDR 0x02020000
#define IMX6UL_UART2_BASE_ADDR 0x021e8000
#define IMX6UL_UART3_BASE_ADDR 0x021ec000
#define IMX6UL_UART4_BASE_ADDR 0x021f0000
#define IMX6UL_UART5_BASE_ADDR 0x021f4000
#define IMX6UL_UART6_BASE_ADDR 0x021fc000
#define IMX6UL_UART7_BASE_ADDR 0x02018000
#define IMX6UL_UART8_BASE_ADDR 0x02024000
#define IMX6UL_UART_BASE_ADDR(n) IMX6UL_UART##n##_BASE_ADDR
#define IMX6UL_UART_BASE(n) IMX6UL_UART_BASE_ADDR(n)
#define IMX7D_UART1_BASE_ADDR 0x30860000
#define IMX7D_UART2_BASE_ADDR 0x30890000
#define IMX7D_UART3_BASE_ADDR 0x30880000
@ -124,6 +135,8 @@
#define UART_PADDR IMX_DEBUG_UART_BASE(IMX6SL)
#elif defined(CONFIG_DEBUG_IMX6SX_UART)
#define UART_PADDR IMX_DEBUG_UART_BASE(IMX6SX)
#elif defined(CONFIG_DEBUG_IMX6UL_UART)
#define UART_PADDR IMX_DEBUG_UART_BASE(IMX6UL)
#elif defined(CONFIG_DEBUG_IMX7D_UART)
#define UART_PADDR IMX_DEBUG_UART_BASE(IMX7D)

View File

@ -548,6 +548,14 @@ config SOC_IMX6SX
help
This enables support for Freescale i.MX6 SoloX processor.
config SOC_IMX6UL
bool "i.MX6 UltraLite support"
select PINCTRL_IMX6UL
select SOC_IMX6
help
This enables support for Freescale i.MX6 UltraLite processor.
config SOC_IMX7D
bool "i.MX7 Dual support"
select PINCTRL_IMX7D

View File

@ -83,6 +83,7 @@ endif
obj-$(CONFIG_SOC_IMX6Q) += mach-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o
obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o
obj-$(CONFIG_SOC_IMX6UL) += mach-imx6ul.o
obj-$(CONFIG_SOC_IMX7D) += mach-imx7d.o
ifeq ($(CONFIG_SUSPEND),y)

View File

@ -130,6 +130,9 @@ struct device * __init imx_soc_device_init(void)
case MXC_CPU_IMX6Q:
soc_id = "i.MX6Q";
break;
case MXC_CPU_IMX6UL:
soc_id = "i.MX6UL";
break;
case MXC_CPU_IMX7D:
soc_id = "i.MX7D";
break;

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2015 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 version 2 as
* published by the Free Software Foundation.
*/
#include <linux/irqchip.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/micrel_phy.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/regmap.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "common.h"
static void __init imx6ul_enet_clk_init(void)
{
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1, IMX6UL_GPR1_ENET_CLK_DIR,
IMX6UL_GPR1_ENET_CLK_OUTPUT);
else
pr_err("failed to find fsl,imx6ul-iomux-gpr regmap\n");
}
static int ksz8081_phy_fixup(struct phy_device *dev)
{
if (dev && dev->interface == PHY_INTERFACE_MODE_MII) {
phy_write(dev, 0x1f, 0x8110);
phy_write(dev, 0x16, 0x201);
} else if (dev && dev->interface == PHY_INTERFACE_MODE_RMII) {
phy_write(dev, 0x1f, 0x8190);
phy_write(dev, 0x16, 0x202);
}
return 0;
}
static void __init imx6ul_enet_phy_init(void)
{
phy_register_fixup_for_uid(PHY_ID_KSZ8081, 0xffffffff, ksz8081_phy_fixup);
}
static inline void imx6ul_enet_init(void)
{
imx6ul_enet_clk_init();
imx6ul_enet_phy_init();
}
static void __init imx6ul_init_machine(void)
{
struct device *parent;
parent = imx_soc_device_init();
if (parent == NULL)
pr_warn("failed to initialize soc device\n");
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
imx6ul_enet_init();
imx_anatop_init();
}
static void __init imx6ul_init_irq(void)
{
imx_init_revision_from_anatop();
imx_src_init();
irqchip_init();
}
static const char *imx6ul_dt_compat[] __initconst = {
"fsl,imx6ul",
NULL,
};
DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.dt_compat = imx6ul_dt_compat,
MACHINE_END

View File

@ -38,6 +38,7 @@
#define MXC_CPU_IMX6DL 0x61
#define MXC_CPU_IMX6SX 0x62
#define MXC_CPU_IMX6Q 0x63
#define MXC_CPU_IMX6UL 0x64
#define MXC_CPU_IMX7D 0x72
#define IMX_DDR_TYPE_LPDDR2 1
@ -165,6 +166,11 @@ static inline bool cpu_is_imx6sx(void)
return __mxc_cpu_type == MXC_CPU_IMX6SX;
}
static inline bool cpu_is_imx6ul(void)
{
return __mxc_cpu_type == MXC_CPU_IMX6UL;
}
static inline bool cpu_is_imx6q(void)
{
return __mxc_cpu_type == MXC_CPU_IMX6Q;

View File

@ -401,6 +401,17 @@ config KEYBOARD_MPR121
To compile this driver as a module, choose M here: the
module will be called mpr121_touchkey.
config KEYBOARD_SNVS_PWRKEY
tristate "IMX SNVS Power Key Driver"
depends on SOC_IMX6SX
depends on OF
help
This is the snvs powerkey driver for the Freescale i.MX application
processors that are newer than i.MX6 SX.
To compile this driver as a module, choose M here; the
module will be called snvs_pwrkey.
config KEYBOARD_IMX
tristate "IMX keypad support"
depends on ARCH_MXC

View File

@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY) += snvs_pwrkey.o
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o

View File

@ -0,0 +1,227 @@
/*
* Driver for the IMX SNVS ON/OFF Power Key
* Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#define SNVS_LPSR_REG 0x4C /* LP Status Register */
#define SNVS_LPCR_REG 0x38 /* LP Control Register */
#define SNVS_HPSR_REG 0x14
#define SNVS_HPSR_BTN BIT(6)
#define SNVS_LPSR_SPO BIT(18)
#define SNVS_LPCR_DEP_EN BIT(5)
#define DEBOUNCE_TIME 30
#define REPEAT_INTERVAL 60
struct pwrkey_drv_data {
struct regmap *snvs;
int irq;
int keycode;
int keystate; /* 1:pressed */
int wakeup;
struct timer_list check_timer;
struct input_dev *input;
};
static void imx_imx_snvs_check_for_events(unsigned long data)
{
struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
struct input_dev *input = pdata->input;
u32 state;
regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
state = state & SNVS_HPSR_BTN ? 1 : 0;
/* only report new event if status changed */
if (state ^ pdata->keystate) {
pdata->keystate = state;
input_event(input, EV_KEY, pdata->keycode, state);
input_sync(input);
pm_relax(pdata->input->dev.parent);
}
/* repeat check if pressed long */
if (state) {
mod_timer(&pdata->check_timer,
jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
}
}
static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
u32 lp_status;
pm_wakeup_event(pdata->input->dev.parent, 0);
regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
if (lp_status & SNVS_LPSR_SPO)
mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
/* clear SPO status */
regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
return IRQ_HANDLED;
}
static void imx_snvs_pwrkey_act(void *pdata)
{
struct pwrkey_drv_data *pd = pdata;
del_timer_sync(&pd->check_timer);
}
static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
{
struct pwrkey_drv_data *pdata = NULL;
struct input_dev *input = NULL;
struct device_node *np;
int error;
/* Get SNVS register Page */
np = pdev->dev.of_node;
if (!np)
return -ENODEV;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");;
if (!pdata->snvs) {
dev_err(&pdev->dev, "Can't get snvs syscon\n");
return -ENODEV;
}
if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
pdata->keycode = KEY_POWER;
dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
}
pdata->wakeup = of_property_read_bool(np, "wakeup-source");
pdata->irq = platform_get_irq(pdev, 0);
if (pdata->irq < 0) {
dev_err(&pdev->dev, "no irq defined in platform data\n");
return -EINVAL;
}
regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
/* clear the unexpected interrupt before driver ready */
regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
setup_timer(&pdata->check_timer,
imx_imx_snvs_check_for_events, (unsigned long) pdata);
input = devm_input_allocate_device(&pdev->dev);
if (!input) {
dev_err(&pdev->dev, "failed to allocate the input device\n");
return -ENOMEM;
}
input->name = pdev->name;
input->phys = "snvs-pwrkey/input0";
input->id.bustype = BUS_HOST;
input_set_capability(input, EV_KEY, pdata->keycode);
/* input customer action to cancel release timer */
error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
if (error) {
dev_err(&pdev->dev, "failed to register remove action\n");
return error;
}
error = devm_request_irq(&pdev->dev, pdata->irq,
imx_snvs_pwrkey_interrupt,
0, pdev->name, pdev);
if (error) {
dev_err(&pdev->dev, "interrupt not available.\n");
return error;
}
error = input_register_device(input);
if (error < 0) {
dev_err(&pdev->dev, "failed to register input device\n");
input_free_device(input);
return error;
}
pdata->input = input;
platform_set_drvdata(pdev, pdata);
device_init_wakeup(&pdev->dev, pdata->wakeup);
return 0;
}
static int imx_snvs_pwrkey_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(pdata->irq);
return 0;
}
static int imx_snvs_pwrkey_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(pdata->irq);
return 0;
}
static const struct of_device_id imx_snvs_pwrkey_ids[] = {
{ .compatible = "fsl,sec-v4.0-pwrkey" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
imx_snvs_pwrkey_resume);
static struct platform_driver imx_snvs_pwrkey_driver = {
.driver = {
.name = "snvs_pwrkey",
.pm = &imx_snvs_pwrkey_pm_ops,
.of_match_table = imx_snvs_pwrkey_ids,
},
.probe = imx_snvs_pwrkey_probe,
};
module_platform_driver(imx_snvs_pwrkey_driver);
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("i.MX snvs power key Driver");
MODULE_LICENSE("GPL");

View File

@ -1523,6 +1523,7 @@ config RTC_DRV_MXC
config RTC_DRV_SNVS
tristate "Freescale SNVS RTC support"
select REGMAP_MMIO
depends on HAS_IOMEM
depends on OF
help

View File

@ -16,6 +16,8 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_device.h>
#define RTC_INPUT_CLK_32768HZ (0x00 << 5)
#define RTC_INPUT_CLK_32000HZ (0x01 << 5)
@ -79,7 +81,8 @@ struct rtc_plat_data {
struct rtc_device *rtc;
void __iomem *ioaddr;
int irq;
struct clk *clk;
struct clk *clk_ref;
struct clk *clk_ipg;
struct rtc_time g_rtc_alarm;
enum imx_rtc_type devtype;
};
@ -97,6 +100,15 @@ static const struct platform_device_id imx_rtc_devtype[] = {
};
MODULE_DEVICE_TABLE(platform, imx_rtc_devtype);
#ifdef CONFIG_OF
static const struct of_device_id imx_rtc_dt_ids[] = {
{ .compatible = "fsl,imx1-rtc", .data = (const void *)IMX1_RTC },
{ .compatible = "fsl,imx21-rtc", .data = (const void *)IMX21_RTC },
{}
};
MODULE_DEVICE_TABLE(of, imx_rtc_dt_ids);
#endif
static inline int is_imx1_rtc(struct rtc_plat_data *data)
{
return data->devtype == IMX1_RTC;
@ -361,29 +373,45 @@ static int mxc_rtc_probe(struct platform_device *pdev)
u32 reg;
unsigned long rate;
int ret;
const struct of_device_id *of_id;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->devtype = pdev->id_entry->driver_data;
of_id = of_match_device(imx_rtc_dt_ids, &pdev->dev);
if (of_id)
pdata->devtype = (enum imx_rtc_type)of_id->data;
else
pdata->devtype = pdev->id_entry->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pdata->ioaddr))
return PTR_ERR(pdata->ioaddr);
pdata->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pdata->clk)) {
dev_err(&pdev->dev, "unable to get clock!\n");
return PTR_ERR(pdata->clk);
pdata->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(pdata->clk_ipg)) {
dev_err(&pdev->dev, "unable to get ipg clock!\n");
return PTR_ERR(pdata->clk_ipg);
}
ret = clk_prepare_enable(pdata->clk);
ret = clk_prepare_enable(pdata->clk_ipg);
if (ret)
return ret;
rate = clk_get_rate(pdata->clk);
pdata->clk_ref = devm_clk_get(&pdev->dev, "ref");
if (IS_ERR(pdata->clk_ref)) {
dev_err(&pdev->dev, "unable to get ref clock!\n");
ret = PTR_ERR(pdata->clk_ref);
goto exit_put_clk_ipg;
}
ret = clk_prepare_enable(pdata->clk_ref);
if (ret)
goto exit_put_clk_ipg;
rate = clk_get_rate(pdata->clk_ref);
if (rate == 32768)
reg = RTC_INPUT_CLK_32768HZ;
@ -394,7 +422,7 @@ static int mxc_rtc_probe(struct platform_device *pdev)
else {
dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", rate);
ret = -EINVAL;
goto exit_put_clk;
goto exit_put_clk_ref;
}
reg |= RTC_ENABLE_BIT;
@ -402,7 +430,7 @@ static int mxc_rtc_probe(struct platform_device *pdev)
if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) {
dev_err(&pdev->dev, "hardware module can't be enabled!\n");
ret = -EIO;
goto exit_put_clk;
goto exit_put_clk_ref;
}
platform_set_drvdata(pdev, pdata);
@ -424,15 +452,17 @@ static int mxc_rtc_probe(struct platform_device *pdev)
THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
goto exit_put_clk;
goto exit_put_clk_ref;
}
pdata->rtc = rtc;
return 0;
exit_put_clk:
clk_disable_unprepare(pdata->clk);
exit_put_clk_ref:
clk_disable_unprepare(pdata->clk_ref);
exit_put_clk_ipg:
clk_disable_unprepare(pdata->clk_ipg);
return ret;
}
@ -441,7 +471,8 @@ static int mxc_rtc_remove(struct platform_device *pdev)
{
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
clk_disable_unprepare(pdata->clk);
clk_disable_unprepare(pdata->clk_ref);
clk_disable_unprepare(pdata->clk_ipg);
return 0;
}
@ -473,6 +504,7 @@ static SIMPLE_DEV_PM_OPS(mxc_rtc_pm_ops, mxc_rtc_suspend, mxc_rtc_resume);
static struct platform_driver mxc_rtc_driver = {
.driver = {
.name = "mxc_rtc",
.of_match_table = of_match_ptr(imx_rtc_dt_ids),
.pm = &mxc_rtc_pm_ops,
},
.id_table = imx_rtc_devtype,

View File

@ -18,6 +18,10 @@
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#define SNVS_LPREGISTER_OFFSET 0x34
/* These register offsets are relative to LP (Low Power) range */
#define SNVS_LPCR 0x04
@ -37,31 +41,36 @@
struct snvs_rtc_data {
struct rtc_device *rtc;
void __iomem *ioaddr;
struct regmap *regmap;
int offset;
int irq;
spinlock_t lock;
struct clk *clk;
};
static u32 rtc_read_lp_counter(void __iomem *ioaddr)
static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
{
u64 read1, read2;
u32 val;
do {
read1 = readl(ioaddr + SNVS_LPSRTCMR);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
read1 = val;
read1 <<= 32;
read1 |= readl(ioaddr + SNVS_LPSRTCLR);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
read1 |= val;
read2 = readl(ioaddr + SNVS_LPSRTCMR);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
read2 = val;
read2 <<= 32;
read2 |= readl(ioaddr + SNVS_LPSRTCLR);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
read2 |= val;
} while (read1 != read2);
/* Convert 47-bit counter to 32-bit raw second count */
return (u32) (read1 >> CNTR_TO_SECS_SH);
}
static void rtc_write_sync_lp(void __iomem *ioaddr)
static void rtc_write_sync_lp(struct snvs_rtc_data *data)
{
u32 count1, count2, count3;
int i;
@ -69,15 +78,15 @@ static void rtc_write_sync_lp(void __iomem *ioaddr)
/* Wait for 3 CKIL cycles */
for (i = 0; i < 3; i++) {
do {
count1 = readl(ioaddr + SNVS_LPSRTCLR);
count2 = readl(ioaddr + SNVS_LPSRTCLR);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);
} while (count1 != count2);
/* Now wait until counter value changes */
do {
do {
count2 = readl(ioaddr + SNVS_LPSRTCLR);
count3 = readl(ioaddr + SNVS_LPSRTCLR);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);
regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3);
} while (count2 != count3);
} while (count3 == count1);
}
@ -85,23 +94,14 @@ static void rtc_write_sync_lp(void __iomem *ioaddr)
static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
{
unsigned long flags;
int timeout = 1000;
u32 lpcr;
spin_lock_irqsave(&data->lock, flags);
lpcr = readl(data->ioaddr + SNVS_LPCR);
if (enable)
lpcr |= SNVS_LPCR_SRTC_ENV;
else
lpcr &= ~SNVS_LPCR_SRTC_ENV;
writel(lpcr, data->ioaddr + SNVS_LPCR);
spin_unlock_irqrestore(&data->lock, flags);
regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV,
enable ? SNVS_LPCR_SRTC_ENV : 0);
while (--timeout) {
lpcr = readl(data->ioaddr + SNVS_LPCR);
regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr);
if (enable) {
if (lpcr & SNVS_LPCR_SRTC_ENV)
@ -121,7 +121,7 @@ static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
unsigned long time = rtc_read_lp_counter(data->ioaddr);
unsigned long time = rtc_read_lp_counter(data);
rtc_time_to_tm(time, tm);
@ -139,8 +139,8 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
snvs_rtc_enable(data, false);
/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
/* Enable RTC again */
snvs_rtc_enable(data, true);
@ -153,10 +153,10 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct snvs_rtc_data *data = dev_get_drvdata(dev);
u32 lptar, lpsr;
lptar = readl(data->ioaddr + SNVS_LPTAR);
regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);
rtc_time_to_tm(lptar, &alrm->time);
lpsr = readl(data->ioaddr + SNVS_LPSR);
regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
return 0;
@ -165,21 +165,12 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
{
struct snvs_rtc_data *data = dev_get_drvdata(dev);
u32 lpcr;
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
regmap_update_bits(data->regmap, data->offset + SNVS_LPCR,
(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN),
enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0);
lpcr = readl(data->ioaddr + SNVS_LPCR);
if (enable)
lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
else
lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
writel(lpcr, data->ioaddr + SNVS_LPCR);
spin_unlock_irqrestore(&data->lock, flags);
rtc_write_sync_lp(data->ioaddr);
rtc_write_sync_lp(data);
return 0;
}
@ -189,24 +180,14 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct snvs_rtc_data *data = dev_get_drvdata(dev);
struct rtc_time *alrm_tm = &alrm->time;
unsigned long time;
unsigned long flags;
u32 lpcr;
rtc_tm_to_time(alrm_tm, &time);
spin_lock_irqsave(&data->lock, flags);
/* Have to clear LPTA_EN before programming new alarm time in LPTAR */
lpcr = readl(data->ioaddr + SNVS_LPCR);
lpcr &= ~SNVS_LPCR_LPTA_EN;
writel(lpcr, data->ioaddr + SNVS_LPCR);
spin_unlock_irqrestore(&data->lock, flags);
writel(time, data->ioaddr + SNVS_LPTAR);
regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
/* Clear alarm interrupt status bit */
writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA);
return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
}
@ -226,7 +207,7 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
u32 lpsr;
u32 events = 0;
lpsr = readl(data->ioaddr + SNVS_LPSR);
regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
if (lpsr & SNVS_LPSR_LPTA) {
events |= (RTC_AF | RTC_IRQF);
@ -238,25 +219,48 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
}
/* clear interrupt status */
writel(lpsr, data->ioaddr + SNVS_LPSR);
regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
return events ? IRQ_HANDLED : IRQ_NONE;
}
static const struct regmap_config snvs_rtc_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int snvs_rtc_probe(struct platform_device *pdev)
{
struct snvs_rtc_data *data;
struct resource *res;
int ret;
void __iomem *mmio;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->ioaddr))
return PTR_ERR(data->ioaddr);
data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
if (IS_ERR(data->regmap)) {
dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmio = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);
} else {
data->offset = SNVS_LPREGISTER_OFFSET;
of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
}
if (!data->regmap) {
dev_err(&pdev->dev, "Can't find snvs syscon\n");
return -ENODEV;
}
data->irq = platform_get_irq(pdev, 0);
if (data->irq < 0)
@ -276,13 +280,11 @@ static int snvs_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
spin_lock_init(&data->lock);
/* Initialize glitch detect */
writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);
/* Clear interrupt status */
writel(0xffffffff, data->ioaddr + SNVS_LPSR);
regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
/* Enable RTC */
snvs_rtc_enable(data, true);

View File

@ -435,4 +435,12 @@
#define IMX6SX_GPR5_DISP_MUX_DCIC1_LVDS (0x1 << 1)
#define IMX6SX_GPR5_DISP_MUX_DCIC1_MASK (0x1 << 1)
/* For imx6ul iomux gpr register field define */
#define IMX6UL_GPR1_ENET1_CLK_DIR (0x1 << 17)
#define IMX6UL_GPR1_ENET2_CLK_DIR (0x1 << 18)
#define IMX6UL_GPR1_ENET1_CLK_OUTPUT (0x1 << 17)
#define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18)
#define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17)
#define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17)
#endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */