mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 21:54:11 +08:00
Immutable branch between MFD and HWMON due for the v4.18 merge window
-----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJa1JE2AAoJEFGvii+H/Hdh0asQAK0hUgpLw9PabNYobrN6FMzH tsotCyCIuMDMakONtVj9ijZ7Urrj2rCsPC1282ZlTnzbLxqTyguQWDZQh70dxBo5 CMeuQWm1BLflPSmI/uF0nvgmiSN0CJJtH8NbyIXAoe6NGMI+sjPBtNy69GYH7QO0 TJXa5m6+lA79clnXZOeN0aLKMNc1Z+jwZEXr0G30DsHRClhDgEAHodag0vITiGOr bnBeWivxdvJWF9gdxPaUr0HvDjVQATzpa0vtZfjjDoTlDOMa+4P2ow7I1fADDEDp S1AERTgFIwQsUZPuxKvSjZZMZqCbKvbJuTCko9NgOBc6FVDNMl0SQpb4QrpmKUIn 1wsFZEh6O7gZB8zz2Lw/tVbZTDK9KJUGPcl6FX2vjy3GX+jo9yO5ya2/Fy7iGQRn YjaXprt4G4w/qMBQGbvAJN0avMy7YoDJTO4biCOSRbw4W7GnV6xFRisKImVAdRk9 IuiIUlpGO7IfZ0MYC07WqGa4HfINf35m24L1kJpymM2XO+QtzcyMemOtR6or6O9s xneXkvGBmJLUKyGoCQH/a9PVXTwvWfl0ycREbklxloZOque5hr42CueykezJ1NR5 v0HfIp95HOp5sPMVXlW10O63hTkt2r/4U33c5y7+CzQtkBbBYTJrpTD3/poFr8D+ srJEd7OjWytOfSLZA/FO =r+0D -----END PGP SIGNATURE----- mergetag object55d1d1547a
type commit tag ib-mfd-input-rtc-v4.18 tagger Lee Jones <lee.jones@linaro.org> 1523888198 +0100 Immutable branch between MFD, Input and RTC due for the v4.18 merge window -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJa1LBGAAoJEFGvii+H/Hdh63YP/j1rQr7DgbbYpxp8bglggY9M ZVdRcoDRFjxLl+WajbLVxAHF1ajWQpREIQwqgbPdTCJhxvSoLhAPfhjkZPki0rzj GIn9M7qFB1wifjSwmCwD6dAH91q8qEXBM/BxWcWCXGliVfB3cSNDcdIEitwWBR1k +7i0TtGfkmoHrLsmNCDcn+1Uc/n+jUnivFTs6xoyWsCLOq7gMTR+WS6n+nU69tX0 GDc301DMs6eWIekLD/0i+cy4qe99j9cphqssKeg1Ks91KebkpsBPTe+a6Dr09JNV +Ka2Rk2+WNK4UFqs8rlrvrLhB/QfpTiOmGkg2c7A3hGUgMB/QxMUovc6APKI+FE+ M9IetUMF1up+6sjxXy5DxYOkIL3jboUcXiNRCgHLyDswaWcCtAb8jEtRO1NY2jIQ vHHilIOBzrlGfULd3HrIyivlXdkz4st1zWjlsj+vkDlOowwuPOWFf15G0gtZBR4w rig+MQTGqpn5zc8bDKlKYh6vIl0EKXzLxfD/IDAZkEDxPffOordyDETUtyoHZLR8 pDALRSzvg7h4wAGZzXdYTOnCZjcgUc2xtSHc4yTMPljlNdK6YHIeK5sXBWAZMKbi mv0Pxjb/0LqE9W+gduEzsHUx8BqqsedSkILTW1lqTgXB2SzYmXbPDBhRtfTC+B4A xo8cco8AkwXuctt94WQx =O7G8 -----END PGP SIGNATURE----- mergetag objectab3a897847
type commit tag ib-mfd-pwm-v4.18 tagger Lee Jones <lee.jones@linaro.org> 1526458673 +0100 Immutable branch between MFD and PWM due for the v4.18 merge window -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAlr76TEACgkQUa+KL4f8 d2E0Pw/+M7vC+Ks0TsJfEoKdzSX+LWwyo/zDDZXmawIGvNqnjqGcO0fa1iaux//Y UCb/qO5I/DJO86y89NQSU/K0nH2fB7us+Ny5bjySRtcG435yo2cWMD3rJnsW2pb3 myPPx3EK19zuhzQ2cRWkzFqP9Dhy79SzfNlOyoI1AryyH3rfH2IXZ1RufqNFb6LP 0mAKonG225i/t8ta8dTJukp6IcbTUMtKKRRAnZ6YQqZQRyvCGfG1tmIQZxWKAejP NfTrvN1bA5fufP3jRKjkmo3v94YjQFa+05h+fXCXzc1ds7+HOgvDX7XO4Cf4sFGD sEW6YuZaDGq+Zkl5I7gyqDoT7jdfBumTJbfcq5ERYF6UPjdOlo9vIHCXW/JVIXEk IVUsS3lZaC+vcp3EqUG8QxQnjEVSyXMbsPSERmo0VOe7eutqcPNS+YB+GzWMzu9H AVr7f1eLCu3mNMr2Jgbvwk0F8SLDn2RHZVJ3+smoicQ5okvwaojZAuA2gXD8N90J mXSuqIsjYZXq00LxCd6KsZHiGJbfAlka5eN+5669Z+yUQs47cjR2F1gw5wKjRoFC h/plbnTcIjNY/jvBvfLyr0MzSb2/NKFrzqnQm25PkgOasPrpkBwcR6FKZq+cjrhX 7mmf34Xqv0j5MZsY+LL3XrTzrRY9+EK68zs8M+aCAee85RieN9s= =SU5E -----END PGP SIGNATURE----- Merge tags 'ib-mfd-hwmon-v4.18', 'ib-mfd-input-rtc-v4.18' and 'ib-mfd-pwm-v4.18' into ibs-for-mfd-merged Immutable branch between MFD and HWMON due for the v4.18 merge window Immutable branch between MFD, Input and RTC due for the v4.18 merge window Immutable branch between MFD and PWM due for the v4.18 merge window
This commit is contained in:
commit
e3691b7143
43
Documentation/devicetree/bindings/input/mtk-pmic-keys.txt
Normal file
43
Documentation/devicetree/bindings/input/mtk-pmic-keys.txt
Normal file
@ -0,0 +1,43 @@
|
||||
MediaTek MT6397/MT6323 PMIC Keys Device Driver
|
||||
|
||||
There are two key functions provided by MT6397/MT6323 PMIC, pwrkey
|
||||
and homekey. The key functions are defined as the subnode of the function
|
||||
node provided by MT6397/MT6323 PMIC that is being defined as one kind
|
||||
of Muti-Function Device (MFD)
|
||||
|
||||
For MT6397/MT6323 MFD bindings see:
|
||||
Documentation/devicetree/bindings/mfd/mt6397.txt
|
||||
|
||||
Required properties:
|
||||
- compatible: "mediatek,mt6397-keys" or "mediatek,mt6323-keys"
|
||||
- linux,keycodes: See Documentation/devicetree/bindings/input/keys.txt
|
||||
|
||||
Optional Properties:
|
||||
- wakeup-source: See Documentation/devicetree/bindings/power/wakeup-source.txt
|
||||
- mediatek,long-press-mode: Long press key shutdown setting, 1 for
|
||||
pwrkey only, 2 for pwrkey/homekey together, others for disabled.
|
||||
- power-off-time-sec: See Documentation/devicetree/bindings/input/keys.txt
|
||||
|
||||
Example:
|
||||
|
||||
pmic: mt6397 {
|
||||
compatible = "mediatek,mt6397";
|
||||
|
||||
...
|
||||
|
||||
mt6397keys: mt6397keys {
|
||||
compatible = "mediatek,mt6397-keys";
|
||||
mediatek,long-press-mode = <1>;
|
||||
power-off-time-sec = <0>;
|
||||
|
||||
power {
|
||||
linux,keycodes = <116>;
|
||||
wakeup-source;
|
||||
};
|
||||
|
||||
home {
|
||||
linux,keycodes = <114>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
@ -7,6 +7,7 @@ MT6397/MT6323 is a multifunction device with the following sub modules:
|
||||
- GPIO
|
||||
- Clock
|
||||
- LED
|
||||
- Keys
|
||||
|
||||
It is interfaced to host controller using SPI interface by a proprietary hardware
|
||||
called PMIC wrapper or pwrap. MT6397/MT6323 MFD is a child device of pwrap.
|
||||
@ -40,6 +41,11 @@ Optional subnodes:
|
||||
- compatible: "mediatek,mt6323-led"
|
||||
see Documentation/devicetree/bindings/leds/leds-mt6323.txt
|
||||
|
||||
- keys
|
||||
Required properties:
|
||||
- compatible: "mediatek,mt6397-keys" or "mediatek,mt6323-keys"
|
||||
see Documentation/devicetree/bindings/input/mtk-pmic-keys.txt
|
||||
|
||||
Example:
|
||||
pwrap: pwrap@1000f000 {
|
||||
compatible = "mediatek,mt8135-pwrap";
|
||||
|
@ -747,4 +747,13 @@ config KEYBOARD_BCM
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bcm-keypad.
|
||||
|
||||
config KEYBOARD_MTK_PMIC
|
||||
tristate "MediaTek PMIC keys support"
|
||||
depends on MFD_MT6397
|
||||
help
|
||||
Say Y here if you want to use the pmic keys (powerkey/homekey).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pmic-keys.
|
||||
|
||||
endif
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o
|
||||
|
339
drivers/input/keyboard/mtk-pmic-keys.c
Normal file
339
drivers/input/keyboard/mtk-pmic-keys.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (C) 2017 MediaTek, Inc.
|
||||
*
|
||||
* Author: Chen Zhong <chen.zhong@mediatek.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/mt6323/registers.h>
|
||||
#include <linux/mfd/mt6397/registers.h>
|
||||
#include <linux/mfd/mt6397/core.h>
|
||||
|
||||
#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1
|
||||
#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6
|
||||
#define MTK_PMIC_HOMEKEY_RST_EN_MASK 0x1
|
||||
#define MTK_PMIC_HOMEKEY_RST_EN_SHIFT 5
|
||||
#define MTK_PMIC_RST_DU_MASK 0x3
|
||||
#define MTK_PMIC_RST_DU_SHIFT 8
|
||||
|
||||
#define MTK_PMIC_PWRKEY_RST \
|
||||
(MTK_PMIC_PWRKEY_RST_EN_MASK << MTK_PMIC_PWRKEY_RST_EN_SHIFT)
|
||||
#define MTK_PMIC_HOMEKEY_RST \
|
||||
(MTK_PMIC_HOMEKEY_RST_EN_MASK << MTK_PMIC_HOMEKEY_RST_EN_SHIFT)
|
||||
|
||||
#define MTK_PMIC_PWRKEY_INDEX 0
|
||||
#define MTK_PMIC_HOMEKEY_INDEX 1
|
||||
#define MTK_PMIC_MAX_KEY_COUNT 2
|
||||
|
||||
struct mtk_pmic_keys_regs {
|
||||
u32 deb_reg;
|
||||
u32 deb_mask;
|
||||
u32 intsel_reg;
|
||||
u32 intsel_mask;
|
||||
};
|
||||
|
||||
#define MTK_PMIC_KEYS_REGS(_deb_reg, _deb_mask, \
|
||||
_intsel_reg, _intsel_mask) \
|
||||
{ \
|
||||
.deb_reg = _deb_reg, \
|
||||
.deb_mask = _deb_mask, \
|
||||
.intsel_reg = _intsel_reg, \
|
||||
.intsel_mask = _intsel_mask, \
|
||||
}
|
||||
|
||||
struct mtk_pmic_regs {
|
||||
const struct mtk_pmic_keys_regs keys_regs[MTK_PMIC_MAX_KEY_COUNT];
|
||||
u32 pmic_rst_reg;
|
||||
};
|
||||
|
||||
static const struct mtk_pmic_regs mt6397_regs = {
|
||||
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6397_CHRSTATUS,
|
||||
0x8, MT6397_INT_RSV, 0x10),
|
||||
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6397_OCSTATUS2,
|
||||
0x10, MT6397_INT_RSV, 0x8),
|
||||
.pmic_rst_reg = MT6397_TOP_RST_MISC,
|
||||
};
|
||||
|
||||
static const struct mtk_pmic_regs mt6323_regs = {
|
||||
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS,
|
||||
0x2, MT6323_INT_MISC_CON, 0x10),
|
||||
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS,
|
||||
0x4, MT6323_INT_MISC_CON, 0x8),
|
||||
.pmic_rst_reg = MT6323_TOP_RST_MISC,
|
||||
};
|
||||
|
||||
struct mtk_pmic_keys_info {
|
||||
struct mtk_pmic_keys *keys;
|
||||
const struct mtk_pmic_keys_regs *regs;
|
||||
unsigned int keycode;
|
||||
int irq;
|
||||
bool wakeup:1;
|
||||
};
|
||||
|
||||
struct mtk_pmic_keys {
|
||||
struct input_dev *input_dev;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mtk_pmic_keys_info keys[MTK_PMIC_MAX_KEY_COUNT];
|
||||
};
|
||||
|
||||
enum mtk_pmic_keys_lp_mode {
|
||||
LP_DISABLE,
|
||||
LP_ONEKEY,
|
||||
LP_TWOKEY,
|
||||
};
|
||||
|
||||
static void mtk_pmic_keys_lp_reset_setup(struct mtk_pmic_keys *keys,
|
||||
u32 pmic_rst_reg)
|
||||
{
|
||||
int ret;
|
||||
u32 long_press_mode, long_press_debounce;
|
||||
|
||||
ret = of_property_read_u32(keys->dev->of_node,
|
||||
"power-off-time-sec", &long_press_debounce);
|
||||
if (ret)
|
||||
long_press_debounce = 0;
|
||||
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_RST_DU_MASK << MTK_PMIC_RST_DU_SHIFT,
|
||||
long_press_debounce << MTK_PMIC_RST_DU_SHIFT);
|
||||
|
||||
ret = of_property_read_u32(keys->dev->of_node,
|
||||
"mediatek,long-press-mode", &long_press_mode);
|
||||
if (ret)
|
||||
long_press_mode = LP_DISABLE;
|
||||
|
||||
switch (long_press_mode) {
|
||||
case LP_ONEKEY:
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_PWRKEY_RST,
|
||||
MTK_PMIC_PWRKEY_RST);
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_HOMEKEY_RST,
|
||||
0);
|
||||
break;
|
||||
case LP_TWOKEY:
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_PWRKEY_RST,
|
||||
MTK_PMIC_PWRKEY_RST);
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_HOMEKEY_RST,
|
||||
MTK_PMIC_HOMEKEY_RST);
|
||||
break;
|
||||
case LP_DISABLE:
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_PWRKEY_RST,
|
||||
0);
|
||||
regmap_update_bits(keys->regmap, pmic_rst_reg,
|
||||
MTK_PMIC_HOMEKEY_RST,
|
||||
0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t mtk_pmic_keys_irq_handler_thread(int irq, void *data)
|
||||
{
|
||||
struct mtk_pmic_keys_info *info = data;
|
||||
u32 key_deb, pressed;
|
||||
|
||||
regmap_read(info->keys->regmap, info->regs->deb_reg, &key_deb);
|
||||
|
||||
key_deb &= info->regs->deb_mask;
|
||||
|
||||
pressed = !key_deb;
|
||||
|
||||
input_report_key(info->keys->input_dev, info->keycode, pressed);
|
||||
input_sync(info->keys->input_dev);
|
||||
|
||||
dev_dbg(info->keys->dev, "(%s) key =%d using PMIC\n",
|
||||
pressed ? "pressed" : "released", info->keycode);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mtk_pmic_key_setup(struct mtk_pmic_keys *keys,
|
||||
struct mtk_pmic_keys_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
info->keys = keys;
|
||||
|
||||
ret = regmap_update_bits(keys->regmap, info->regs->intsel_reg,
|
||||
info->regs->intsel_mask,
|
||||
info->regs->intsel_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(keys->dev, info->irq, NULL,
|
||||
mtk_pmic_keys_irq_handler_thread,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
|
||||
"mtk-pmic-keys", info);
|
||||
if (ret) {
|
||||
dev_err(keys->dev, "Failed to request IRQ: %d: %d\n",
|
||||
info->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
input_set_capability(keys->input_dev, EV_KEY, info->keycode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_pmic_keys_suspend(struct device *dev)
|
||||
{
|
||||
struct mtk_pmic_keys *keys = dev_get_drvdata(dev);
|
||||
int index;
|
||||
|
||||
for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
|
||||
if (keys->keys[index].wakeup)
|
||||
enable_irq_wake(keys->keys[index].irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_pmic_keys_resume(struct device *dev)
|
||||
{
|
||||
struct mtk_pmic_keys *keys = dev_get_drvdata(dev);
|
||||
int index;
|
||||
|
||||
for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
|
||||
if (keys->keys[index].wakeup)
|
||||
disable_irq_wake(keys->keys[index].irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mtk_pmic_keys_pm_ops, mtk_pmic_keys_suspend,
|
||||
mtk_pmic_keys_resume);
|
||||
|
||||
static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt6397-keys",
|
||||
.data = &mt6397_regs,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6323-keys",
|
||||
.data = &mt6323_regs,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_mtk_pmic_keys_match_tbl);
|
||||
|
||||
static int mtk_pmic_keys_probe(struct platform_device *pdev)
|
||||
{
|
||||
int error, index = 0;
|
||||
unsigned int keycount;
|
||||
struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device_node *node = pdev->dev.of_node, *child;
|
||||
struct mtk_pmic_keys *keys;
|
||||
const struct mtk_pmic_regs *mtk_pmic_regs;
|
||||
struct input_dev *input_dev;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(of_mtk_pmic_keys_match_tbl, &pdev->dev);
|
||||
|
||||
keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
|
||||
if (!keys)
|
||||
return -ENOMEM;
|
||||
|
||||
keys->dev = &pdev->dev;
|
||||
keys->regmap = pmic_chip->regmap;
|
||||
mtk_pmic_regs = of_id->data;
|
||||
|
||||
keys->input_dev = input_dev = devm_input_allocate_device(keys->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(keys->dev, "input allocate device fail.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_dev->name = "mtk-pmic-keys";
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0001;
|
||||
|
||||
keycount = of_get_available_child_count(node);
|
||||
if (keycount > MTK_PMIC_MAX_KEY_COUNT) {
|
||||
dev_err(keys->dev, "too many keys defined (%d)\n", keycount);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index];
|
||||
|
||||
keys->keys[index].irq = platform_get_irq(pdev, index);
|
||||
if (keys->keys[index].irq < 0)
|
||||
return keys->keys[index].irq;
|
||||
|
||||
error = of_property_read_u32(child,
|
||||
"linux,keycodes", &keys->keys[index].keycode);
|
||||
if (error) {
|
||||
dev_err(keys->dev,
|
||||
"failed to read key:%d linux,keycode property: %d\n",
|
||||
index, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(child, "wakeup-source"))
|
||||
keys->keys[index].wakeup = true;
|
||||
|
||||
error = mtk_pmic_key_setup(keys, &keys->keys[index]);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"register input device failed (%d)\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs->pmic_rst_reg);
|
||||
|
||||
platform_set_drvdata(pdev, keys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pmic_keys_pdrv = {
|
||||
.probe = mtk_pmic_keys_probe,
|
||||
.driver = {
|
||||
.name = "mtk-pmic-keys",
|
||||
.of_match_table = of_mtk_pmic_keys_match_tbl,
|
||||
.pm = &mtk_pmic_keys_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pmic_keys_pdrv);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Chen Zhong <chen.zhong@mediatek.com>");
|
||||
MODULE_DESCRIPTION("MTK pmic-keys driver v0.1");
|
@ -43,6 +43,16 @@ static const struct resource mt6397_rtc_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct resource mt6323_keys_resources[] = {
|
||||
DEFINE_RES_IRQ(MT6323_IRQ_STATUS_PWRKEY),
|
||||
DEFINE_RES_IRQ(MT6323_IRQ_STATUS_FCHRKEY),
|
||||
};
|
||||
|
||||
static const struct resource mt6397_keys_resources[] = {
|
||||
DEFINE_RES_IRQ(MT6397_IRQ_PWRKEY),
|
||||
DEFINE_RES_IRQ(MT6397_IRQ_HOMEKEY),
|
||||
};
|
||||
|
||||
static const struct mfd_cell mt6323_devs[] = {
|
||||
{
|
||||
.name = "mt6323-regulator",
|
||||
@ -50,6 +60,11 @@ static const struct mfd_cell mt6323_devs[] = {
|
||||
}, {
|
||||
.name = "mt6323-led",
|
||||
.of_compatible = "mediatek,mt6323-led"
|
||||
}, {
|
||||
.name = "mtk-pmic-keys",
|
||||
.num_resources = ARRAY_SIZE(mt6323_keys_resources),
|
||||
.resources = mt6323_keys_resources,
|
||||
.of_compatible = "mediatek,mt6323-keys"
|
||||
},
|
||||
};
|
||||
|
||||
@ -71,7 +86,12 @@ static const struct mfd_cell mt6397_devs[] = {
|
||||
}, {
|
||||
.name = "mt6397-pinctrl",
|
||||
.of_compatible = "mediatek,mt6397-pinctrl",
|
||||
},
|
||||
}, {
|
||||
.name = "mtk-pmic-keys",
|
||||
.num_resources = ARRAY_SIZE(mt6397_keys_resources),
|
||||
.resources = mt6397_keys_resources,
|
||||
.of_compatible = "mediatek,mt6397-keys"
|
||||
}
|
||||
};
|
||||
|
||||
static void mt6397_irq_lock(struct irq_data *data)
|
||||
@ -289,7 +309,7 @@ static int mt6397_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_mfd_add_devices(&pdev->dev, -1, mt6323_devs,
|
||||
ARRAY_SIZE(mt6323_devs), NULL,
|
||||
0, NULL);
|
||||
0, pmic->irq_domain);
|
||||
break;
|
||||
|
||||
case MT6397_CID_CODE:
|
||||
@ -304,7 +324,7 @@ static int mt6397_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_mfd_add_devices(&pdev->dev, -1, mt6397_devs,
|
||||
ARRAY_SIZE(mt6397_devs), NULL,
|
||||
0, NULL);
|
||||
0, pmic->irq_domain);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -4,16 +4,156 @@
|
||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define STM32_TIMERS_MAX_REGISTERS 0x3fc
|
||||
|
||||
/* DIER register DMA enable bits */
|
||||
static const u32 stm32_timers_dier_dmaen[STM32_TIMERS_MAX_DMAS] = {
|
||||
TIM_DIER_CC1DE,
|
||||
TIM_DIER_CC2DE,
|
||||
TIM_DIER_CC3DE,
|
||||
TIM_DIER_CC4DE,
|
||||
TIM_DIER_UIE,
|
||||
TIM_DIER_TDE,
|
||||
TIM_DIER_COMDE
|
||||
};
|
||||
|
||||
static void stm32_timers_dma_done(void *p)
|
||||
{
|
||||
struct stm32_timers_dma *dma = p;
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
|
||||
status = dmaengine_tx_status(dma->chan, dma->chan->cookie, &state);
|
||||
if (status == DMA_COMPLETE)
|
||||
complete(&dma->completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_timers_dma_burst_read - Read from timers registers using DMA.
|
||||
*
|
||||
* Read from STM32 timers registers using DMA on a single event.
|
||||
* @dev: reference to stm32_timers MFD device
|
||||
* @buf: DMA'able destination buffer
|
||||
* @id: stm32_timers_dmas event identifier (ch[1..4], up, trig or com)
|
||||
* @reg: registers start offset for DMA to read from (like CCRx for capture)
|
||||
* @num_reg: number of registers to read upon each DMA request, starting @reg.
|
||||
* @bursts: number of bursts to read (e.g. like two for pwm period capture)
|
||||
* @tmo_ms: timeout (milliseconds)
|
||||
*/
|
||||
int stm32_timers_dma_burst_read(struct device *dev, u32 *buf,
|
||||
enum stm32_timers_dmas id, u32 reg,
|
||||
unsigned int num_reg, unsigned int bursts,
|
||||
unsigned long tmo_ms)
|
||||
{
|
||||
struct stm32_timers *ddata = dev_get_drvdata(dev);
|
||||
unsigned long timeout = msecs_to_jiffies(tmo_ms);
|
||||
struct regmap *regmap = ddata->regmap;
|
||||
struct stm32_timers_dma *dma = &ddata->dma;
|
||||
size_t len = num_reg * bursts * sizeof(u32);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_slave_config config;
|
||||
dma_cookie_t cookie;
|
||||
dma_addr_t dma_buf;
|
||||
u32 dbl, dba;
|
||||
long err;
|
||||
int ret;
|
||||
|
||||
/* Sanity check */
|
||||
if (id < STM32_TIMERS_DMA_CH1 || id >= STM32_TIMERS_MAX_DMAS)
|
||||
return -EINVAL;
|
||||
|
||||
if (!num_reg || !bursts || reg > STM32_TIMERS_MAX_REGISTERS ||
|
||||
(reg + num_reg * sizeof(u32)) > STM32_TIMERS_MAX_REGISTERS)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dma->chans[id])
|
||||
return -ENODEV;
|
||||
mutex_lock(&dma->lock);
|
||||
|
||||
/* Select DMA channel in use */
|
||||
dma->chan = dma->chans[id];
|
||||
dma_buf = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dev, dma_buf)) {
|
||||
ret = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Prepare DMA read from timer registers, using DMA burst mode */
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.src_addr = (dma_addr_t)dma->phys_base + TIM_DMAR;
|
||||
config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
ret = dmaengine_slave_config(dma->chan, &config);
|
||||
if (ret)
|
||||
goto unmap;
|
||||
|
||||
desc = dmaengine_prep_slave_single(dma->chan, dma_buf, len,
|
||||
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
|
||||
if (!desc) {
|
||||
ret = -EBUSY;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
desc->callback = stm32_timers_dma_done;
|
||||
desc->callback_param = dma;
|
||||
cookie = dmaengine_submit(desc);
|
||||
ret = dma_submit_error(cookie);
|
||||
if (ret)
|
||||
goto dma_term;
|
||||
|
||||
reinit_completion(&dma->completion);
|
||||
dma_async_issue_pending(dma->chan);
|
||||
|
||||
/* Setup and enable timer DMA burst mode */
|
||||
dbl = FIELD_PREP(TIM_DCR_DBL, bursts - 1);
|
||||
dba = FIELD_PREP(TIM_DCR_DBA, reg >> 2);
|
||||
ret = regmap_write(regmap, TIM_DCR, dbl | dba);
|
||||
if (ret)
|
||||
goto dma_term;
|
||||
|
||||
/* Clear pending flags before enabling DMA request */
|
||||
ret = regmap_write(regmap, TIM_SR, 0);
|
||||
if (ret)
|
||||
goto dcr_clr;
|
||||
|
||||
ret = regmap_update_bits(regmap, TIM_DIER, stm32_timers_dier_dmaen[id],
|
||||
stm32_timers_dier_dmaen[id]);
|
||||
if (ret)
|
||||
goto dcr_clr;
|
||||
|
||||
err = wait_for_completion_interruptible_timeout(&dma->completion,
|
||||
timeout);
|
||||
if (err == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
else if (err < 0)
|
||||
ret = err;
|
||||
|
||||
regmap_update_bits(regmap, TIM_DIER, stm32_timers_dier_dmaen[id], 0);
|
||||
regmap_write(regmap, TIM_SR, 0);
|
||||
dcr_clr:
|
||||
regmap_write(regmap, TIM_DCR, 0);
|
||||
dma_term:
|
||||
dmaengine_terminate_all(dma->chan);
|
||||
unmap:
|
||||
dma_unmap_single(dev, dma_buf, len, DMA_FROM_DEVICE);
|
||||
unlock:
|
||||
dma->chan = NULL;
|
||||
mutex_unlock(&dma->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stm32_timers_dma_burst_read);
|
||||
|
||||
static const struct regmap_config stm32_timers_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = sizeof(u32),
|
||||
.max_register = 0x3fc,
|
||||
.max_register = STM32_TIMERS_MAX_REGISTERS,
|
||||
};
|
||||
|
||||
static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
|
||||
@ -27,12 +167,45 @@ static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
|
||||
regmap_write(ddata->regmap, TIM_ARR, 0x0);
|
||||
}
|
||||
|
||||
static void stm32_timers_dma_probe(struct device *dev,
|
||||
struct stm32_timers *ddata)
|
||||
{
|
||||
int i;
|
||||
char name[4];
|
||||
|
||||
init_completion(&ddata->dma.completion);
|
||||
mutex_init(&ddata->dma.lock);
|
||||
|
||||
/* Optional DMA support: get valid DMA channel(s) or NULL */
|
||||
for (i = STM32_TIMERS_DMA_CH1; i <= STM32_TIMERS_DMA_CH4; i++) {
|
||||
snprintf(name, ARRAY_SIZE(name), "ch%1d", i + 1);
|
||||
ddata->dma.chans[i] = dma_request_slave_channel(dev, name);
|
||||
}
|
||||
ddata->dma.chans[STM32_TIMERS_DMA_UP] =
|
||||
dma_request_slave_channel(dev, "up");
|
||||
ddata->dma.chans[STM32_TIMERS_DMA_TRIG] =
|
||||
dma_request_slave_channel(dev, "trig");
|
||||
ddata->dma.chans[STM32_TIMERS_DMA_COM] =
|
||||
dma_request_slave_channel(dev, "com");
|
||||
}
|
||||
|
||||
static void stm32_timers_dma_remove(struct device *dev,
|
||||
struct stm32_timers *ddata)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = STM32_TIMERS_DMA_CH1; i < STM32_TIMERS_MAX_DMAS; i++)
|
||||
if (ddata->dma.chans[i])
|
||||
dma_release_channel(ddata->dma.chans[i]);
|
||||
}
|
||||
|
||||
static int stm32_timers_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_timers *ddata;
|
||||
struct resource *res;
|
||||
void __iomem *mmio;
|
||||
int ret;
|
||||
|
||||
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
@ -43,6 +216,9 @@ static int stm32_timers_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mmio))
|
||||
return PTR_ERR(mmio);
|
||||
|
||||
/* Timer physical addr for DMA */
|
||||
ddata->dma.phys_base = res->start;
|
||||
|
||||
ddata->regmap = devm_regmap_init_mmio_clk(dev, "int", mmio,
|
||||
&stm32_timers_regmap_cfg);
|
||||
if (IS_ERR(ddata->regmap))
|
||||
@ -54,9 +230,29 @@ static int stm32_timers_probe(struct platform_device *pdev)
|
||||
|
||||
stm32_timers_get_arr_size(ddata);
|
||||
|
||||
stm32_timers_dma_probe(dev, ddata);
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
return devm_of_platform_populate(&pdev->dev);
|
||||
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
if (ret)
|
||||
stm32_timers_dma_remove(dev, ddata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_timers_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_timers *ddata = platform_get_drvdata(pdev);
|
||||
|
||||
/*
|
||||
* Don't use devm_ here: enfore of_platform_depopulate() happens before
|
||||
* DMA are released, to avoid race on DMA.
|
||||
*/
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
stm32_timers_dma_remove(&pdev->dev, ddata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_timers_of_match[] = {
|
||||
@ -67,6 +263,7 @@ MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
|
||||
|
||||
static struct platform_driver stm32_timers_driver = {
|
||||
.probe = stm32_timers_probe,
|
||||
.remove = stm32_timers_remove,
|
||||
.driver = {
|
||||
.name = "stm32-timers",
|
||||
.of_match_table = stm32_timers_of_match,
|
||||
|
@ -8,6 +8,7 @@
|
||||
* pwm-atmel.c from Bo Shen
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -25,6 +26,7 @@ struct stm32_pwm {
|
||||
struct regmap *regmap;
|
||||
u32 max_arr;
|
||||
bool have_complementary_output;
|
||||
u32 capture[4] ____cacheline_aligned; /* DMA'able buffer */
|
||||
};
|
||||
|
||||
struct stm32_breakinput {
|
||||
@ -62,6 +64,258 @@ static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define TIM_CCER_CC12P (TIM_CCER_CC1P | TIM_CCER_CC2P)
|
||||
#define TIM_CCER_CC12E (TIM_CCER_CC1E | TIM_CCER_CC2E)
|
||||
#define TIM_CCER_CC34P (TIM_CCER_CC3P | TIM_CCER_CC4P)
|
||||
#define TIM_CCER_CC34E (TIM_CCER_CC3E | TIM_CCER_CC4E)
|
||||
|
||||
/*
|
||||
* Capture using PWM input mode:
|
||||
* ___ ___
|
||||
* TI[1, 2, 3 or 4]: ........._| |________|
|
||||
* ^0 ^1 ^2
|
||||
* . . .
|
||||
* . . XXXXX
|
||||
* . . XXXXX |
|
||||
* . XXXXX . |
|
||||
* XXXXX . . |
|
||||
* COUNTER: ______XXXXX . . . |_XXX
|
||||
* start^ . . . ^stop
|
||||
* . . . .
|
||||
* v v . v
|
||||
* v
|
||||
* CCR1/CCR3: tx..........t0...........t2
|
||||
* CCR2/CCR4: tx..............t1.........
|
||||
*
|
||||
* DMA burst transfer: | |
|
||||
* v v
|
||||
* DMA buffer: { t0, tx } { t2, t1 }
|
||||
* DMA done: ^
|
||||
*
|
||||
* 0: IC1/3 snapchot on rising edge: counter value -> CCR1/CCR3
|
||||
* + DMA transfer CCR[1/3] & CCR[2/4] values (t0, tx: doesn't care)
|
||||
* 1: IC2/4 snapchot on falling edge: counter value -> CCR2/CCR4
|
||||
* 2: IC1/3 snapchot on rising edge: counter value -> CCR1/CCR3
|
||||
* + DMA transfer CCR[1/3] & CCR[2/4] values (t2, t1)
|
||||
*
|
||||
* DMA done, compute:
|
||||
* - Period = t2 - t0
|
||||
* - Duty cycle = t1 - t0
|
||||
*/
|
||||
static int stm32_pwm_raw_capture(struct stm32_pwm *priv, struct pwm_device *pwm,
|
||||
unsigned long tmo_ms, u32 *raw_prd,
|
||||
u32 *raw_dty)
|
||||
{
|
||||
struct device *parent = priv->chip.dev->parent;
|
||||
enum stm32_timers_dmas dma_id;
|
||||
u32 ccen, ccr;
|
||||
int ret;
|
||||
|
||||
/* Ensure registers have been updated, enable counter and capture */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
|
||||
|
||||
/* Use cc1 or cc3 DMA resp for PWM input channels 1 & 2 or 3 & 4 */
|
||||
dma_id = pwm->hwpwm < 2 ? STM32_TIMERS_DMA_CH1 : STM32_TIMERS_DMA_CH3;
|
||||
ccen = pwm->hwpwm < 2 ? TIM_CCER_CC12E : TIM_CCER_CC34E;
|
||||
ccr = pwm->hwpwm < 2 ? TIM_CCR1 : TIM_CCR3;
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, ccen, ccen);
|
||||
|
||||
/*
|
||||
* Timer DMA burst mode. Request 2 registers, 2 bursts, to get both
|
||||
* CCR1 & CCR2 (or CCR3 & CCR4) on each capture event.
|
||||
* We'll get two capture snapchots: { CCR1, CCR2 }, { CCR1, CCR2 }
|
||||
* or { CCR3, CCR4 }, { CCR3, CCR4 }
|
||||
*/
|
||||
ret = stm32_timers_dma_burst_read(parent, priv->capture, dma_id, ccr, 2,
|
||||
2, tmo_ms);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
/* Period: t2 - t0 (take care of counter overflow) */
|
||||
if (priv->capture[0] <= priv->capture[2])
|
||||
*raw_prd = priv->capture[2] - priv->capture[0];
|
||||
else
|
||||
*raw_prd = priv->max_arr - priv->capture[0] + priv->capture[2];
|
||||
|
||||
/* Duty cycle capture requires at least two capture units */
|
||||
if (pwm->chip->npwm < 2)
|
||||
*raw_dty = 0;
|
||||
else if (priv->capture[0] <= priv->capture[3])
|
||||
*raw_dty = priv->capture[3] - priv->capture[0];
|
||||
else
|
||||
*raw_dty = priv->max_arr - priv->capture[0] + priv->capture[3];
|
||||
|
||||
if (*raw_dty > *raw_prd) {
|
||||
/*
|
||||
* Race beetween PWM input and DMA: it may happen
|
||||
* falling edge triggers new capture on TI2/4 before DMA
|
||||
* had a chance to read CCR2/4. It means capture[1]
|
||||
* contains period + duty_cycle. So, subtract period.
|
||||
*/
|
||||
*raw_dty -= *raw_prd;
|
||||
}
|
||||
|
||||
stop:
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, ccen, 0);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_capture *result, unsigned long tmo_ms)
|
||||
{
|
||||
struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
|
||||
unsigned long long prd, div, dty;
|
||||
unsigned long rate;
|
||||
unsigned int psc = 0, icpsc, scale;
|
||||
u32 raw_prd, raw_dty;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
if (active_channels(priv)) {
|
||||
ret = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(priv->chip.dev, "failed to enable counter clock\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(priv->clk);
|
||||
if (!rate) {
|
||||
ret = -EINVAL;
|
||||
goto clk_dis;
|
||||
}
|
||||
|
||||
/* prescaler: fit timeout window provided by upper layer */
|
||||
div = (unsigned long long)rate * (unsigned long long)tmo_ms;
|
||||
do_div(div, MSEC_PER_SEC);
|
||||
prd = div;
|
||||
while ((div > priv->max_arr) && (psc < MAX_TIM_PSC)) {
|
||||
psc++;
|
||||
div = prd;
|
||||
do_div(div, psc + 1);
|
||||
}
|
||||
regmap_write(priv->regmap, TIM_ARR, priv->max_arr);
|
||||
regmap_write(priv->regmap, TIM_PSC, psc);
|
||||
|
||||
/* Map TI1 or TI2 PWM input to IC1 & IC2 (or TI3/4 to IC3 & IC4) */
|
||||
regmap_update_bits(priv->regmap,
|
||||
pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
|
||||
TIM_CCMR_CC1S | TIM_CCMR_CC2S, pwm->hwpwm & 0x1 ?
|
||||
TIM_CCMR_CC1S_TI2 | TIM_CCMR_CC2S_TI2 :
|
||||
TIM_CCMR_CC1S_TI1 | TIM_CCMR_CC2S_TI1);
|
||||
|
||||
/* Capture period on IC1/3 rising edge, duty cycle on IC2/4 falling. */
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, pwm->hwpwm < 2 ?
|
||||
TIM_CCER_CC12P : TIM_CCER_CC34P, pwm->hwpwm < 2 ?
|
||||
TIM_CCER_CC2P : TIM_CCER_CC4P);
|
||||
|
||||
ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
/*
|
||||
* Got a capture. Try to improve accuracy at high rates:
|
||||
* - decrease counter clock prescaler, scale up to max rate.
|
||||
* - use input prescaler, capture once every /2 /4 or /8 edges.
|
||||
*/
|
||||
if (raw_prd) {
|
||||
u32 max_arr = priv->max_arr - 0x1000; /* arbitrary margin */
|
||||
|
||||
scale = max_arr / min(max_arr, raw_prd);
|
||||
} else {
|
||||
scale = priv->max_arr; /* bellow resolution, use max scale */
|
||||
}
|
||||
|
||||
if (psc && scale > 1) {
|
||||
/* 2nd measure with new scale */
|
||||
psc /= scale;
|
||||
regmap_write(priv->regmap, TIM_PSC, psc);
|
||||
ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd,
|
||||
&raw_dty);
|
||||
if (ret)
|
||||
goto stop;
|
||||
}
|
||||
|
||||
/* Compute intermediate period not to exceed timeout at low rates */
|
||||
prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
|
||||
do_div(prd, rate);
|
||||
|
||||
for (icpsc = 0; icpsc < MAX_TIM_ICPSC ; icpsc++) {
|
||||
/* input prescaler: also keep arbitrary margin */
|
||||
if (raw_prd >= (priv->max_arr - 0x1000) >> (icpsc + 1))
|
||||
break;
|
||||
if (prd >= (tmo_ms * NSEC_PER_MSEC) >> (icpsc + 2))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!icpsc)
|
||||
goto done;
|
||||
|
||||
/* Last chance to improve period accuracy, using input prescaler */
|
||||
regmap_update_bits(priv->regmap,
|
||||
pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
|
||||
TIM_CCMR_IC1PSC | TIM_CCMR_IC2PSC,
|
||||
FIELD_PREP(TIM_CCMR_IC1PSC, icpsc) |
|
||||
FIELD_PREP(TIM_CCMR_IC2PSC, icpsc));
|
||||
|
||||
ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
if (raw_dty >= (raw_prd >> icpsc)) {
|
||||
/*
|
||||
* We may fall here using input prescaler, when input
|
||||
* capture starts on high side (before falling edge).
|
||||
* Example with icpsc to capture on each 4 events:
|
||||
*
|
||||
* start 1st capture 2nd capture
|
||||
* v v v
|
||||
* ___ _____ _____ _____ _____ ____
|
||||
* TI1..4 |__| |__| |__| |__| |__|
|
||||
* v v . . . . . v v
|
||||
* icpsc1/3: . 0 . 1 . 2 . 3 . 0
|
||||
* icpsc2/4: 0 1 2 3 0
|
||||
* v v v v
|
||||
* CCR1/3 ......t0..............................t2
|
||||
* CCR2/4 ..t1..............................t1'...
|
||||
* . . .
|
||||
* Capture0: .<----------------------------->.
|
||||
* Capture1: .<-------------------------->. .
|
||||
* . . .
|
||||
* Period: .<------> . .
|
||||
* Low side: .<>.
|
||||
*
|
||||
* Result:
|
||||
* - Period = Capture0 / icpsc
|
||||
* - Duty = Period - Low side = Period - (Capture0 - Capture1)
|
||||
*/
|
||||
raw_dty = (raw_prd >> icpsc) - (raw_prd - raw_dty);
|
||||
}
|
||||
|
||||
done:
|
||||
prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
|
||||
result->period = DIV_ROUND_UP_ULL(prd, rate << icpsc);
|
||||
dty = (unsigned long long)raw_dty * (psc + 1) * NSEC_PER_SEC;
|
||||
result->duty_cycle = DIV_ROUND_UP_ULL(dty, rate);
|
||||
stop:
|
||||
regmap_write(priv->regmap, TIM_CCER, 0);
|
||||
regmap_write(priv->regmap, pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2, 0);
|
||||
regmap_write(priv->regmap, TIM_PSC, 0);
|
||||
clk_dis:
|
||||
clk_disable(priv->clk);
|
||||
unlock:
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_pwm_config(struct stm32_pwm *priv, int ch,
|
||||
int duty_ns, int period_ns)
|
||||
{
|
||||
@ -230,6 +484,9 @@ static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
static const struct pwm_ops stm32pwm_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.apply = stm32_pwm_apply_locked,
|
||||
#if IS_ENABLED(CONFIG_DMA_ENGINE)
|
||||
.capture = stm32_pwm_capture,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int stm32_pwm_set_breakinput(struct stm32_pwm *priv,
|
||||
|
@ -322,10 +322,9 @@ static int mtk_rtc_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rtc->addr_base = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
rtc->irq = irq_create_mapping(mt6397_chip->irq_domain, res->start);
|
||||
if (rtc->irq <= 0)
|
||||
return -EINVAL;
|
||||
rtc->irq = platform_get_irq(pdev, 0);
|
||||
if (rtc->irq < 0)
|
||||
return rtc->irq;
|
||||
|
||||
rtc->regmap = mt6397_chip->regmap;
|
||||
rtc->dev = &pdev->dev;
|
||||
|
@ -8,6 +8,8 @@
|
||||
#define _LINUX_STM32_GPTIMER_H_
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define TIM_CR1 0x00 /* Control Register 1 */
|
||||
@ -27,6 +29,8 @@
|
||||
#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */
|
||||
#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */
|
||||
#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
|
||||
#define TIM_DCR 0x48 /* DMA control register */
|
||||
#define TIM_DMAR 0x4C /* DMA register for transfer */
|
||||
|
||||
#define TIM_CR1_CEN BIT(0) /* Counter Enable */
|
||||
#define TIM_CR1_DIR BIT(4) /* Counter Direction */
|
||||
@ -36,17 +40,35 @@
|
||||
#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
|
||||
#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */
|
||||
#define TIM_DIER_UIE BIT(0) /* Update interrupt */
|
||||
#define TIM_DIER_UDE BIT(8) /* Update DMA request Enable */
|
||||
#define TIM_DIER_CC1DE BIT(9) /* CC1 DMA request Enable */
|
||||
#define TIM_DIER_CC2DE BIT(10) /* CC2 DMA request Enable */
|
||||
#define TIM_DIER_CC3DE BIT(11) /* CC3 DMA request Enable */
|
||||
#define TIM_DIER_CC4DE BIT(12) /* CC4 DMA request Enable */
|
||||
#define TIM_DIER_COMDE BIT(13) /* COM DMA request Enable */
|
||||
#define TIM_DIER_TDE BIT(14) /* Trigger DMA request Enable */
|
||||
#define TIM_SR_UIF BIT(0) /* Update interrupt flag */
|
||||
#define TIM_EGR_UG BIT(0) /* Update Generation */
|
||||
#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */
|
||||
#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */
|
||||
#define TIM_CCMR_CC1S (BIT(0) | BIT(1)) /* Capture/compare 1 sel */
|
||||
#define TIM_CCMR_IC1PSC GENMASK(3, 2) /* Input capture 1 prescaler */
|
||||
#define TIM_CCMR_CC2S (BIT(8) | BIT(9)) /* Capture/compare 2 sel */
|
||||
#define TIM_CCMR_IC2PSC GENMASK(11, 10) /* Input capture 2 prescaler */
|
||||
#define TIM_CCMR_CC1S_TI1 BIT(0) /* IC1/IC3 selects TI1/TI3 */
|
||||
#define TIM_CCMR_CC1S_TI2 BIT(1) /* IC1/IC3 selects TI2/TI4 */
|
||||
#define TIM_CCMR_CC2S_TI2 BIT(8) /* IC2/IC4 selects TI2/TI4 */
|
||||
#define TIM_CCMR_CC2S_TI1 BIT(9) /* IC2/IC4 selects TI1/TI3 */
|
||||
#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
|
||||
#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
|
||||
#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */
|
||||
#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
|
||||
#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */
|
||||
#define TIM_CCER_CC2P BIT(5) /* Capt/Comp 2 Polarity */
|
||||
#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */
|
||||
#define TIM_CCER_CC3P BIT(9) /* Capt/Comp 3 Polarity */
|
||||
#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */
|
||||
#define TIM_CCER_CC4P BIT(13) /* Capt/Comp 4 Polarity */
|
||||
#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
|
||||
#define TIM_BDTR_BKE BIT(12) /* Break input enable */
|
||||
#define TIM_BDTR_BKP BIT(13) /* Break input polarity */
|
||||
@ -56,8 +78,11 @@
|
||||
#define TIM_BDTR_BK2F (BIT(20) | BIT(21) | BIT(22) | BIT(23))
|
||||
#define TIM_BDTR_BK2E BIT(24) /* Break 2 input enable */
|
||||
#define TIM_BDTR_BK2P BIT(25) /* Break 2 input polarity */
|
||||
#define TIM_DCR_DBA GENMASK(4, 0) /* DMA base addr */
|
||||
#define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */
|
||||
|
||||
#define MAX_TIM_PSC 0xFFFF
|
||||
#define MAX_TIM_ICPSC 0x3
|
||||
#define TIM_CR2_MMS_SHIFT 4
|
||||
#define TIM_CR2_MMS2_SHIFT 20
|
||||
#define TIM_SMCR_TS_SHIFT 4
|
||||
@ -65,9 +90,42 @@
|
||||
#define TIM_BDTR_BKF_SHIFT 16
|
||||
#define TIM_BDTR_BK2F_SHIFT 20
|
||||
|
||||
enum stm32_timers_dmas {
|
||||
STM32_TIMERS_DMA_CH1,
|
||||
STM32_TIMERS_DMA_CH2,
|
||||
STM32_TIMERS_DMA_CH3,
|
||||
STM32_TIMERS_DMA_CH4,
|
||||
STM32_TIMERS_DMA_UP,
|
||||
STM32_TIMERS_DMA_TRIG,
|
||||
STM32_TIMERS_DMA_COM,
|
||||
STM32_TIMERS_MAX_DMAS,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_timers_dma - STM32 timer DMA handling.
|
||||
* @completion: end of DMA transfer completion
|
||||
* @phys_base: control registers physical base address
|
||||
* @lock: protect DMA access
|
||||
* @chan: DMA channel in use
|
||||
* @chans: DMA channels available for this timer instance
|
||||
*/
|
||||
struct stm32_timers_dma {
|
||||
struct completion completion;
|
||||
phys_addr_t phys_base;
|
||||
struct mutex lock;
|
||||
struct dma_chan *chan;
|
||||
struct dma_chan *chans[STM32_TIMERS_MAX_DMAS];
|
||||
};
|
||||
|
||||
struct stm32_timers {
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
u32 max_arr;
|
||||
struct stm32_timers_dma dma; /* Only to be used by the parent */
|
||||
};
|
||||
|
||||
int stm32_timers_dma_burst_read(struct device *dev, u32 *buf,
|
||||
enum stm32_timers_dmas id, u32 reg,
|
||||
unsigned int num_reg, unsigned int bursts,
|
||||
unsigned long tmo_ms);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user