mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 14:44:10 +08:00
pinctrl: mediatek: Add Pinctrl/GPIO driver for mt8135.
The mediatek SoCs have GPIO controller that handle both the muxing and GPIOs. The GPIO controller have pinmux, pull enable, pull select, direction and output high/low control. This driver include common driver and mt8135 part. The common driver include the pinctrl driver and GPIO driver. The mt8135 part contain its special device data. Signed-off-by: Hongzhou Yang <hongzhou.yang@mediatek.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
ddac96118f
commit
a6df410d42
@ -205,6 +205,7 @@ source "drivers/pinctrl/sh-pfc/Kconfig"
|
||||
source "drivers/pinctrl/spear/Kconfig"
|
||||
source "drivers/pinctrl/sunxi/Kconfig"
|
||||
source "drivers/pinctrl/vt8500/Kconfig"
|
||||
source "drivers/pinctrl/mediatek/Kconfig"
|
||||
|
||||
config PINCTRL_XWAY
|
||||
bool
|
||||
|
@ -49,3 +49,4 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_ARCH_VT8500) += vt8500/
|
||||
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
|
||||
|
14
drivers/pinctrl/mediatek/Kconfig
Normal file
14
drivers/pinctrl/mediatek/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
if ARCH_MEDIATEK
|
||||
|
||||
config PINCTRL_MTK_COMMON
|
||||
bool
|
||||
select PINMUX
|
||||
select GENERIC_PINCONF
|
||||
select GPIOLIB
|
||||
select OF_GPIO
|
||||
|
||||
config PINCTRL_MT8135
|
||||
def_bool MACH_MT8135
|
||||
select PINCTRL_MTK_COMMON
|
||||
|
||||
endif
|
5
drivers/pinctrl/mediatek/Makefile
Normal file
5
drivers/pinctrl/mediatek/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# Core
|
||||
obj-$(CONFIG_PINCTRL_MTK_COMMON) += pinctrl-mtk-common.o
|
||||
|
||||
# SoC Drivers
|
||||
obj-$(CONFIG_PINCTRL_MT8135) += pinctrl-mt8135.o
|
350
drivers/pinctrl/mediatek/pinctrl-mt8135.c
Normal file
350
drivers/pinctrl/mediatek/pinctrl-mt8135.c
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: Hongzhou.Yang <hongzhou.yang@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <dt-bindings/pinctrl/mt65xx.h>
|
||||
|
||||
#include "pinctrl-mtk-common.h"
|
||||
#include "pinctrl-mtk-mt8135.h"
|
||||
|
||||
#define DRV_BASE1 0x500
|
||||
#define DRV_BASE2 0x510
|
||||
#define PUPD_BASE1 0x400
|
||||
#define PUPD_BASE2 0x450
|
||||
#define R0_BASE1 0x4d0
|
||||
#define R1_BASE1 0x200
|
||||
#define R1_BASE2 0x250
|
||||
|
||||
struct mtk_spec_pull_set {
|
||||
unsigned int pin;
|
||||
unsigned int pupd_offset;
|
||||
unsigned char pupd_bit;
|
||||
unsigned int r0_offset;
|
||||
unsigned char r0_bit;
|
||||
unsigned int r1_offset;
|
||||
unsigned char r1_bit;
|
||||
};
|
||||
|
||||
#define SPEC_PULL(_pin, _pupd_offset, _pupd_bit, _r0_offset, \
|
||||
_r0_bit, _r1_offset, _r1_bit) \
|
||||
{ \
|
||||
.pin = _pin, \
|
||||
.pupd_offset = _pupd_offset, \
|
||||
.pupd_bit = _pupd_bit, \
|
||||
.r0_offset = _r0_offset, \
|
||||
.r0_bit = _r0_bit, \
|
||||
.r1_offset = _r1_offset, \
|
||||
.r1_bit = _r1_bit, \
|
||||
}
|
||||
|
||||
static const struct mtk_drv_group_desc mt8135_drv_grp[] = {
|
||||
/* E8E4E2 2/4/6/8/10/12/14/16 */
|
||||
MTK_DRV_GRP(2, 16, 0, 2, 2),
|
||||
/* E8E4 4/8/12/16 */
|
||||
MTK_DRV_GRP(4, 16, 1, 2, 4),
|
||||
/* E4E2 2/4/6/8 */
|
||||
MTK_DRV_GRP(2, 8, 0, 1, 2),
|
||||
/* E16E8E4 4/8/12/16/20/24/28/32 */
|
||||
MTK_DRV_GRP(4, 32, 0, 2, 4)
|
||||
};
|
||||
|
||||
static const struct mtk_pin_drv_grp mt8135_pin_drv[] = {
|
||||
MTK_PIN_DRV_GRP(0, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(1, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(2, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(3, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(4, DRV_BASE1, 4, 0),
|
||||
MTK_PIN_DRV_GRP(5, DRV_BASE1, 8, 0),
|
||||
MTK_PIN_DRV_GRP(6, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(7, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(8, DRV_BASE1, 0, 0),
|
||||
MTK_PIN_DRV_GRP(9, DRV_BASE1, 0, 0),
|
||||
|
||||
MTK_PIN_DRV_GRP(10, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(11, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(12, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(13, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(14, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(15, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(16, DRV_BASE1, 12, 1),
|
||||
MTK_PIN_DRV_GRP(17, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(18, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(19, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(20, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(21, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(22, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(23, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(24, DRV_BASE1, 16, 1),
|
||||
MTK_PIN_DRV_GRP(33, DRV_BASE1, 24, 1),
|
||||
MTK_PIN_DRV_GRP(34, DRV_BASE2, 12, 2),
|
||||
MTK_PIN_DRV_GRP(37, DRV_BASE2, 20, 1),
|
||||
MTK_PIN_DRV_GRP(38, DRV_BASE2, 20, 1),
|
||||
MTK_PIN_DRV_GRP(39, DRV_BASE2, 20, 1),
|
||||
MTK_PIN_DRV_GRP(40, DRV_BASE2, 24, 1),
|
||||
MTK_PIN_DRV_GRP(41, DRV_BASE2, 24, 1),
|
||||
MTK_PIN_DRV_GRP(42, DRV_BASE2, 24, 1),
|
||||
MTK_PIN_DRV_GRP(43, DRV_BASE2, 28, 1),
|
||||
MTK_PIN_DRV_GRP(44, DRV_BASE2, 28, 1),
|
||||
MTK_PIN_DRV_GRP(45, DRV_BASE2, 28, 1),
|
||||
MTK_PIN_DRV_GRP(46, DRV_BASE2, 28, 1),
|
||||
MTK_PIN_DRV_GRP(47, DRV_BASE2, 28, 1),
|
||||
|
||||
MTK_PIN_DRV_GRP(49, DRV_BASE2+0x10, 0, 1),
|
||||
MTK_PIN_DRV_GRP(50, DRV_BASE2+0x10, 4, 1),
|
||||
MTK_PIN_DRV_GRP(51, DRV_BASE2+0x10, 8, 1),
|
||||
MTK_PIN_DRV_GRP(52, DRV_BASE2+0x10, 12, 2),
|
||||
MTK_PIN_DRV_GRP(53, DRV_BASE2+0x10, 16, 1),
|
||||
MTK_PIN_DRV_GRP(54, DRV_BASE2+0x10, 20, 1),
|
||||
MTK_PIN_DRV_GRP(55, DRV_BASE2+0x10, 24, 1),
|
||||
MTK_PIN_DRV_GRP(56, DRV_BASE2+0x10, 28, 1),
|
||||
|
||||
MTK_PIN_DRV_GRP(57, DRV_BASE2+0x20, 0, 1),
|
||||
MTK_PIN_DRV_GRP(58, DRV_BASE2+0x20, 0, 1),
|
||||
MTK_PIN_DRV_GRP(59, DRV_BASE2+0x20, 0, 1),
|
||||
MTK_PIN_DRV_GRP(60, DRV_BASE2+0x20, 0, 1),
|
||||
MTK_PIN_DRV_GRP(61, DRV_BASE2+0x20, 0, 1),
|
||||
MTK_PIN_DRV_GRP(62, DRV_BASE2+0x20, 0, 1),
|
||||
MTK_PIN_DRV_GRP(63, DRV_BASE2+0x20, 4, 1),
|
||||
MTK_PIN_DRV_GRP(64, DRV_BASE2+0x20, 8, 1),
|
||||
MTK_PIN_DRV_GRP(65, DRV_BASE2+0x20, 12, 1),
|
||||
MTK_PIN_DRV_GRP(66, DRV_BASE2+0x20, 16, 1),
|
||||
MTK_PIN_DRV_GRP(67, DRV_BASE2+0x20, 20, 1),
|
||||
MTK_PIN_DRV_GRP(68, DRV_BASE2+0x20, 24, 1),
|
||||
MTK_PIN_DRV_GRP(69, DRV_BASE2+0x20, 28, 1),
|
||||
|
||||
MTK_PIN_DRV_GRP(70, DRV_BASE2+0x30, 0, 1),
|
||||
MTK_PIN_DRV_GRP(71, DRV_BASE2+0x30, 4, 1),
|
||||
MTK_PIN_DRV_GRP(72, DRV_BASE2+0x30, 8, 1),
|
||||
MTK_PIN_DRV_GRP(73, DRV_BASE2+0x30, 12, 1),
|
||||
MTK_PIN_DRV_GRP(74, DRV_BASE2+0x30, 16, 1),
|
||||
MTK_PIN_DRV_GRP(75, DRV_BASE2+0x30, 20, 1),
|
||||
MTK_PIN_DRV_GRP(76, DRV_BASE2+0x30, 24, 1),
|
||||
MTK_PIN_DRV_GRP(77, DRV_BASE2+0x30, 28, 3),
|
||||
MTK_PIN_DRV_GRP(78, DRV_BASE2+0x30, 28, 3),
|
||||
|
||||
MTK_PIN_DRV_GRP(79, DRV_BASE2+0x40, 0, 3),
|
||||
MTK_PIN_DRV_GRP(80, DRV_BASE2+0x40, 4, 3),
|
||||
|
||||
MTK_PIN_DRV_GRP(81, DRV_BASE2+0x30, 28, 3),
|
||||
MTK_PIN_DRV_GRP(82, DRV_BASE2+0x30, 28, 3),
|
||||
|
||||
MTK_PIN_DRV_GRP(83, DRV_BASE2+0x40, 8, 3),
|
||||
MTK_PIN_DRV_GRP(84, DRV_BASE2+0x40, 8, 3),
|
||||
MTK_PIN_DRV_GRP(85, DRV_BASE2+0x40, 12, 3),
|
||||
MTK_PIN_DRV_GRP(86, DRV_BASE2+0x40, 16, 3),
|
||||
MTK_PIN_DRV_GRP(87, DRV_BASE2+0x40, 8, 3),
|
||||
MTK_PIN_DRV_GRP(88, DRV_BASE2+0x40, 8, 3),
|
||||
|
||||
MTK_PIN_DRV_GRP(89, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(90, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(91, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(92, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(93, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(94, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(95, DRV_BASE2+0x50, 12, 0),
|
||||
|
||||
MTK_PIN_DRV_GRP(96, DRV_BASE1+0xb0, 28, 0),
|
||||
|
||||
MTK_PIN_DRV_GRP(97, DRV_BASE2+0x50, 12, 0),
|
||||
MTK_PIN_DRV_GRP(98, DRV_BASE2+0x50, 16, 0),
|
||||
MTK_PIN_DRV_GRP(99, DRV_BASE2+0x50, 20, 1),
|
||||
MTK_PIN_DRV_GRP(102, DRV_BASE2+0x50, 24, 1),
|
||||
MTK_PIN_DRV_GRP(103, DRV_BASE2+0x50, 28, 1),
|
||||
|
||||
|
||||
MTK_PIN_DRV_GRP(104, DRV_BASE2+0x60, 0, 1),
|
||||
MTK_PIN_DRV_GRP(105, DRV_BASE2+0x60, 4, 1),
|
||||
MTK_PIN_DRV_GRP(106, DRV_BASE2+0x60, 4, 1),
|
||||
MTK_PIN_DRV_GRP(107, DRV_BASE2+0x60, 4, 1),
|
||||
MTK_PIN_DRV_GRP(108, DRV_BASE2+0x60, 4, 1),
|
||||
MTK_PIN_DRV_GRP(109, DRV_BASE2+0x60, 8, 2),
|
||||
MTK_PIN_DRV_GRP(110, DRV_BASE2+0x60, 12, 2),
|
||||
MTK_PIN_DRV_GRP(111, DRV_BASE2+0x60, 16, 2),
|
||||
MTK_PIN_DRV_GRP(112, DRV_BASE2+0x60, 20, 2),
|
||||
MTK_PIN_DRV_GRP(113, DRV_BASE2+0x60, 24, 2),
|
||||
MTK_PIN_DRV_GRP(114, DRV_BASE2+0x60, 28, 2),
|
||||
|
||||
MTK_PIN_DRV_GRP(115, DRV_BASE2+0x70, 0, 2),
|
||||
MTK_PIN_DRV_GRP(116, DRV_BASE2+0x70, 4, 2),
|
||||
MTK_PIN_DRV_GRP(117, DRV_BASE2+0x70, 8, 2),
|
||||
MTK_PIN_DRV_GRP(118, DRV_BASE2+0x70, 12, 2),
|
||||
MTK_PIN_DRV_GRP(119, DRV_BASE2+0x70, 16, 2),
|
||||
MTK_PIN_DRV_GRP(120, DRV_BASE2+0x70, 20, 2),
|
||||
|
||||
MTK_PIN_DRV_GRP(181, DRV_BASE1+0xa0, 12, 1),
|
||||
MTK_PIN_DRV_GRP(182, DRV_BASE1+0xa0, 16, 1),
|
||||
MTK_PIN_DRV_GRP(183, DRV_BASE1+0xa0, 20, 1),
|
||||
MTK_PIN_DRV_GRP(184, DRV_BASE1+0xa0, 24, 1),
|
||||
MTK_PIN_DRV_GRP(185, DRV_BASE1+0xa0, 28, 1),
|
||||
|
||||
MTK_PIN_DRV_GRP(186, DRV_BASE1+0xb0, 0, 2),
|
||||
MTK_PIN_DRV_GRP(187, DRV_BASE1+0xb0, 0, 2),
|
||||
MTK_PIN_DRV_GRP(188, DRV_BASE1+0xb0, 0, 2),
|
||||
MTK_PIN_DRV_GRP(189, DRV_BASE1+0xb0, 0, 2),
|
||||
MTK_PIN_DRV_GRP(190, DRV_BASE1+0xb0, 4, 1),
|
||||
MTK_PIN_DRV_GRP(191, DRV_BASE1+0xb0, 8, 1),
|
||||
MTK_PIN_DRV_GRP(192, DRV_BASE1+0xb0, 12, 1),
|
||||
|
||||
MTK_PIN_DRV_GRP(197, DRV_BASE1+0xb0, 16, 0),
|
||||
MTK_PIN_DRV_GRP(198, DRV_BASE1+0xb0, 16, 0),
|
||||
MTK_PIN_DRV_GRP(199, DRV_BASE1+0xb0, 20, 0),
|
||||
MTK_PIN_DRV_GRP(200, DRV_BASE1+0xb0, 24, 0),
|
||||
MTK_PIN_DRV_GRP(201, DRV_BASE1+0xb0, 16, 0),
|
||||
MTK_PIN_DRV_GRP(202, DRV_BASE1+0xb0, 16, 0)
|
||||
};
|
||||
|
||||
static const struct mtk_spec_pull_set spec_pupd[] = {
|
||||
SPEC_PULL(0, PUPD_BASE1, 0, R0_BASE1, 9, R1_BASE1, 0),
|
||||
SPEC_PULL(1, PUPD_BASE1, 1, R0_BASE1, 8, R1_BASE1, 1),
|
||||
SPEC_PULL(2, PUPD_BASE1, 2, R0_BASE1, 7, R1_BASE1, 2),
|
||||
SPEC_PULL(3, PUPD_BASE1, 3, R0_BASE1, 6, R1_BASE1, 3),
|
||||
SPEC_PULL(4, PUPD_BASE1, 4, R0_BASE1, 1, R1_BASE1, 4),
|
||||
SPEC_PULL(5, PUPD_BASE1, 5, R0_BASE1, 0, R1_BASE1, 5),
|
||||
SPEC_PULL(6, PUPD_BASE1, 6, R0_BASE1, 5, R1_BASE1, 6),
|
||||
SPEC_PULL(7, PUPD_BASE1, 7, R0_BASE1, 4, R1_BASE1, 7),
|
||||
SPEC_PULL(8, PUPD_BASE1, 8, R0_BASE1, 3, R1_BASE1, 8),
|
||||
SPEC_PULL(9, PUPD_BASE1, 9, R0_BASE1, 2, R1_BASE1, 9),
|
||||
SPEC_PULL(89, PUPD_BASE2, 9, R0_BASE1, 18, R1_BASE2, 9),
|
||||
SPEC_PULL(90, PUPD_BASE2, 10, R0_BASE1, 19, R1_BASE2, 10),
|
||||
SPEC_PULL(91, PUPD_BASE2, 11, R0_BASE1, 23, R1_BASE2, 11),
|
||||
SPEC_PULL(92, PUPD_BASE2, 12, R0_BASE1, 24, R1_BASE2, 12),
|
||||
SPEC_PULL(93, PUPD_BASE2, 13, R0_BASE1, 25, R1_BASE2, 13),
|
||||
SPEC_PULL(94, PUPD_BASE2, 14, R0_BASE1, 22, R1_BASE2, 14),
|
||||
SPEC_PULL(95, PUPD_BASE2, 15, R0_BASE1, 20, R1_BASE2, 15),
|
||||
SPEC_PULL(96, PUPD_BASE2+0x10, 0, R0_BASE1, 16, R1_BASE2+0x10, 0),
|
||||
SPEC_PULL(97, PUPD_BASE2+0x10, 1, R0_BASE1, 21, R1_BASE2+0x10, 1),
|
||||
SPEC_PULL(98, PUPD_BASE2+0x10, 2, R0_BASE1, 17, R1_BASE2+0x10, 2),
|
||||
SPEC_PULL(197, PUPD_BASE1+0xc0, 5, R0_BASE1, 13, R1_BASE2+0xc0, 5),
|
||||
SPEC_PULL(198, PUPD_BASE2+0xc0, 6, R0_BASE1, 14, R1_BASE2+0xc0, 6),
|
||||
SPEC_PULL(199, PUPD_BASE2+0xc0, 7, R0_BASE1, 11, R1_BASE2+0xc0, 7),
|
||||
SPEC_PULL(200, PUPD_BASE2+0xc0, 8, R0_BASE1, 10, R1_BASE2+0xc0, 8),
|
||||
SPEC_PULL(201, PUPD_BASE2+0xc0, 9, R0_BASE1, 13, R1_BASE2+0xc0, 9),
|
||||
SPEC_PULL(202, PUPD_BASE2+0xc0, 10, R0_BASE1, 12, R1_BASE2+0xc0, 10)
|
||||
};
|
||||
|
||||
static int spec_pull_set(struct regmap *regmap, unsigned int pin,
|
||||
unsigned char align, bool isup, unsigned int r1r0)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int reg_pupd, reg_set_r0, reg_set_r1;
|
||||
unsigned int reg_rst_r0, reg_rst_r1;
|
||||
bool find = false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(spec_pupd); i++) {
|
||||
if (pin == spec_pupd[i].pin) {
|
||||
find = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!find)
|
||||
return -EINVAL;
|
||||
|
||||
if (isup)
|
||||
reg_pupd = spec_pupd[i].pupd_offset + align;
|
||||
else
|
||||
reg_pupd = spec_pupd[i].pupd_offset + (align << 1);
|
||||
|
||||
regmap_write(regmap, reg_pupd, spec_pupd[i].pupd_bit);
|
||||
|
||||
reg_set_r0 = spec_pupd[i].r0_offset + align;
|
||||
reg_rst_r0 = spec_pupd[i].r0_offset + (align << 1);
|
||||
reg_set_r1 = spec_pupd[i].r1_offset + align;
|
||||
reg_rst_r1 = spec_pupd[i].r1_offset + (align << 1);
|
||||
|
||||
switch (r1r0) {
|
||||
case MTK_PUPD_SET_R1R0_00:
|
||||
regmap_write(regmap, reg_rst_r0, spec_pupd[i].r0_bit);
|
||||
regmap_write(regmap, reg_rst_r1, spec_pupd[i].r1_bit);
|
||||
break;
|
||||
case MTK_PUPD_SET_R1R0_01:
|
||||
regmap_write(regmap, reg_set_r0, spec_pupd[i].r0_bit);
|
||||
regmap_write(regmap, reg_rst_r1, spec_pupd[i].r1_bit);
|
||||
break;
|
||||
case MTK_PUPD_SET_R1R0_10:
|
||||
regmap_write(regmap, reg_rst_r0, spec_pupd[i].r0_bit);
|
||||
regmap_write(regmap, reg_set_r1, spec_pupd[i].r1_bit);
|
||||
break;
|
||||
case MTK_PUPD_SET_R1R0_11:
|
||||
regmap_write(regmap, reg_set_r0, spec_pupd[i].r0_bit);
|
||||
regmap_write(regmap, reg_set_r1, spec_pupd[i].r1_bit);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtk_pinctrl_devdata mt8135_pinctrl_data = {
|
||||
.pins = mtk_pins_mt8135,
|
||||
.npins = ARRAY_SIZE(mtk_pins_mt8135),
|
||||
.grp_desc = mt8135_drv_grp,
|
||||
.n_grp_cls = ARRAY_SIZE(mt8135_drv_grp),
|
||||
.pin_drv_grp = mt8135_pin_drv,
|
||||
.n_pin_drv_grps = ARRAY_SIZE(mt8135_pin_drv),
|
||||
.spec_pull_set = spec_pull_set,
|
||||
.dir_offset = 0x0000,
|
||||
.ies_offset = 0x0100,
|
||||
.pullen_offset = 0x0200,
|
||||
.smt_offset = 0x0300,
|
||||
.pullsel_offset = 0x0400,
|
||||
.invser_offset = 0x0600,
|
||||
.dout_offset = 0x0800,
|
||||
.din_offset = 0x0A00,
|
||||
.pinmux_offset = 0x0C00,
|
||||
.type1_start = 34,
|
||||
.type1_end = 149,
|
||||
.port_shf = 4,
|
||||
.port_mask = 0xf,
|
||||
.port_align = 4,
|
||||
};
|
||||
|
||||
static int mt8135_pinctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
return mtk_pctrl_init(pdev, &mt8135_pinctrl_data);
|
||||
}
|
||||
|
||||
static struct of_device_id mt8135_pctrl_match[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt8135-pinctrl",
|
||||
}, {
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt8135_pctrl_match);
|
||||
|
||||
static struct platform_driver mtk_pinctrl_driver = {
|
||||
.probe = mt8135_pinctrl_probe,
|
||||
.driver = {
|
||||
.name = "mediatek-mt8135-pinctrl",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mt8135_pctrl_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mtk_pinctrl_init(void)
|
||||
{
|
||||
return platform_driver_register(&mtk_pinctrl_driver);
|
||||
}
|
||||
|
||||
module_init(mtk_pinctrl_init);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MediaTek Pinctrl Driver");
|
||||
MODULE_AUTHOR("Hongzhou Yang <hongzhou.yang@mediatek.com>");
|
800
drivers/pinctrl/mediatek/pinctrl-mtk-common.c
Normal file
800
drivers/pinctrl/mediatek/pinctrl-mtk-common.c
Normal file
@ -0,0 +1,800 @@
|
||||
/*
|
||||
* mt65xx pinctrl driver based on Allwinner A1X pinctrl driver.
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: Hongzhou.Yang <hongzhou.yang@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinmux.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <dt-bindings/pinctrl/mt65xx.h>
|
||||
|
||||
#include "../core.h"
|
||||
#include "../pinconf.h"
|
||||
#include "../pinctrl-utils.h"
|
||||
#include "pinctrl-mtk-common.h"
|
||||
|
||||
#define MAX_GPIO_MODE_PER_REG 5
|
||||
#define GPIO_MODE_BITS 3
|
||||
|
||||
static const char * const mtk_gpio_functions[] = {
|
||||
"func0", "func1", "func2", "func3",
|
||||
"func4", "func5", "func6", "func7",
|
||||
};
|
||||
|
||||
/*
|
||||
* There are two base address for pull related configuration
|
||||
* in mt8135, and different GPIO pins use different base address.
|
||||
* When pin number greater than type1_start and less than type1_end,
|
||||
* should use the second base address.
|
||||
*/
|
||||
static struct regmap *mtk_get_regmap(struct mtk_pinctrl *pctl,
|
||||
unsigned long pin)
|
||||
{
|
||||
if (pin >= pctl->devdata->type1_start && pin < pctl->devdata->type1_end)
|
||||
return pctl->regmap2;
|
||||
return pctl->regmap1;
|
||||
}
|
||||
|
||||
static unsigned int mtk_get_port(struct mtk_pinctrl *pctl, unsigned long pin)
|
||||
{
|
||||
/* Different SoC has different mask and port shift. */
|
||||
return ((pin >> 4) & pctl->devdata->port_mask)
|
||||
<< pctl->devdata->port_shf;
|
||||
}
|
||||
|
||||
static int mtk_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
|
||||
struct pinctrl_gpio_range *range, unsigned offset,
|
||||
bool input)
|
||||
{
|
||||
unsigned int reg_addr;
|
||||
unsigned int bit;
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
|
||||
bit = BIT(offset & 0xf);
|
||||
|
||||
if (input)
|
||||
/* Different SoC has different alignment offset. */
|
||||
reg_addr = CLR_ADDR(reg_addr, pctl);
|
||||
else
|
||||
reg_addr = SET_ADDR(reg_addr, pctl);
|
||||
|
||||
regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
unsigned int reg_addr;
|
||||
unsigned int bit;
|
||||
struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev);
|
||||
|
||||
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dout_offset;
|
||||
bit = BIT(offset & 0xf);
|
||||
|
||||
if (value)
|
||||
reg_addr = SET_ADDR(reg_addr, pctl);
|
||||
else
|
||||
reg_addr = CLR_ADDR(reg_addr, pctl);
|
||||
|
||||
regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit);
|
||||
}
|
||||
|
||||
static void mtk_pconf_set_ies_smt(struct mtk_pinctrl *pctl, unsigned pin,
|
||||
int value, enum pin_config_param param)
|
||||
{
|
||||
unsigned int reg_addr, offset;
|
||||
unsigned int bit;
|
||||
|
||||
bit = BIT(pin & 0xf);
|
||||
|
||||
if (param == PIN_CONFIG_INPUT_ENABLE)
|
||||
offset = pctl->devdata->ies_offset;
|
||||
else
|
||||
offset = pctl->devdata->smt_offset;
|
||||
|
||||
if (value)
|
||||
reg_addr = SET_ADDR(mtk_get_port(pctl, pin) + offset, pctl);
|
||||
else
|
||||
reg_addr = CLR_ADDR(mtk_get_port(pctl, pin) + offset, pctl);
|
||||
|
||||
regmap_write(mtk_get_regmap(pctl, pin), reg_addr, bit);
|
||||
}
|
||||
|
||||
static const struct mtk_pin_drv_grp *mtk_find_pin_drv_grp_by_pin(
|
||||
struct mtk_pinctrl *pctl, unsigned long pin) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pctl->devdata->n_pin_drv_grps; i++) {
|
||||
const struct mtk_pin_drv_grp *pin_drv =
|
||||
pctl->devdata->pin_drv_grp + i;
|
||||
if (pin == pin_drv->pin)
|
||||
return pin_drv;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mtk_pconf_set_driving(struct mtk_pinctrl *pctl,
|
||||
unsigned int pin, unsigned char driving)
|
||||
{
|
||||
const struct mtk_pin_drv_grp *pin_drv;
|
||||
unsigned int val;
|
||||
unsigned int bits, mask, shift;
|
||||
const struct mtk_drv_group_desc *drv_grp;
|
||||
|
||||
if (pin >= pctl->devdata->npins)
|
||||
return -EINVAL;
|
||||
|
||||
pin_drv = mtk_find_pin_drv_grp_by_pin(pctl, pin);
|
||||
if (!pin_drv || pin_drv->grp > pctl->devdata->n_grp_cls)
|
||||
return -EINVAL;
|
||||
|
||||
drv_grp = pctl->devdata->grp_desc + pin_drv->grp;
|
||||
if (driving >= drv_grp->min_drv && driving <= drv_grp->max_drv
|
||||
&& !(driving % drv_grp->step)) {
|
||||
val = driving / drv_grp->step - 1;
|
||||
bits = drv_grp->high_bit - drv_grp->low_bit + 1;
|
||||
mask = BIT(bits) - 1;
|
||||
shift = pin_drv->bit + drv_grp->low_bit;
|
||||
mask <<= shift;
|
||||
val <<= shift;
|
||||
return regmap_update_bits(mtk_get_regmap(pctl, pin),
|
||||
pin_drv->offset, mask, val);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mtk_pconf_set_pull_select(struct mtk_pinctrl *pctl,
|
||||
unsigned int pin, bool enable, bool isup, unsigned int arg)
|
||||
{
|
||||
unsigned int bit;
|
||||
unsigned int reg_pullen, reg_pullsel;
|
||||
int ret;
|
||||
|
||||
/* Some pins' pull setting are very different,
|
||||
* they have separate pull up/down bit, R0 and R1
|
||||
* resistor bit, so we need this special handle.
|
||||
*/
|
||||
if (pctl->devdata->spec_pull_set) {
|
||||
ret = pctl->devdata->spec_pull_set(mtk_get_regmap(pctl, pin),
|
||||
pin, pctl->devdata->port_align, isup, arg);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For generic pull config, default arg value should be 0 or 1. */
|
||||
if (arg != 0 && arg != 1) {
|
||||
dev_err(pctl->dev, "invalid pull-up argument %d on pin %d .\n",
|
||||
arg, pin);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bit = BIT(pin & 0xf);
|
||||
if (enable)
|
||||
reg_pullen = SET_ADDR(mtk_get_port(pctl, pin) +
|
||||
pctl->devdata->pullen_offset, pctl);
|
||||
else
|
||||
reg_pullen = CLR_ADDR(mtk_get_port(pctl, pin) +
|
||||
pctl->devdata->pullen_offset, pctl);
|
||||
|
||||
if (isup)
|
||||
reg_pullsel = SET_ADDR(mtk_get_port(pctl, pin) +
|
||||
pctl->devdata->pullsel_offset, pctl);
|
||||
else
|
||||
reg_pullsel = CLR_ADDR(mtk_get_port(pctl, pin) +
|
||||
pctl->devdata->pullsel_offset, pctl);
|
||||
|
||||
regmap_write(mtk_get_regmap(pctl, pin), reg_pullen, bit);
|
||||
regmap_write(mtk_get_regmap(pctl, pin), reg_pullsel, bit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pconf_parse_conf(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin, enum pin_config_param param,
|
||||
enum pin_config_param arg)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
switch (param) {
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
mtk_pconf_set_pull_select(pctl, pin, false, false, arg);
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
mtk_pconf_set_pull_select(pctl, pin, true, true, arg);
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
mtk_pconf_set_pull_select(pctl, pin, true, false, arg);
|
||||
break;
|
||||
case PIN_CONFIG_INPUT_ENABLE:
|
||||
mtk_pconf_set_ies_smt(pctl, pin, arg, param);
|
||||
break;
|
||||
case PIN_CONFIG_OUTPUT:
|
||||
mtk_gpio_set(pctl->chip, pin, arg);
|
||||
mtk_pmx_gpio_set_direction(pctldev, NULL, pin, false);
|
||||
break;
|
||||
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
|
||||
mtk_pconf_set_ies_smt(pctl, pin, arg, param);
|
||||
break;
|
||||
case PIN_CONFIG_DRIVE_STRENGTH:
|
||||
mtk_pconf_set_driving(pctl, pin, arg);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pconf_group_get(struct pinctrl_dev *pctldev,
|
||||
unsigned group,
|
||||
unsigned long *config)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
*config = pctl->groups[group].config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group,
|
||||
unsigned long *configs, unsigned num_configs)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
struct mtk_pinctrl_group *g = &pctl->groups[group];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_configs; i++) {
|
||||
mtk_pconf_parse_conf(pctldev, g->pin,
|
||||
pinconf_to_config_param(configs[i]),
|
||||
pinconf_to_config_argument(configs[i]));
|
||||
|
||||
g->config = configs[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinconf_ops mtk_pconf_ops = {
|
||||
.pin_config_group_get = mtk_pconf_group_get,
|
||||
.pin_config_group_set = mtk_pconf_group_set,
|
||||
};
|
||||
|
||||
static struct mtk_pinctrl_group *
|
||||
mtk_pctrl_find_group_by_pin(struct mtk_pinctrl *pctl, u32 pin)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pctl->ngroups; i++) {
|
||||
struct mtk_pinctrl_group *grp = pctl->groups + i;
|
||||
|
||||
if (grp->pin == pin)
|
||||
return grp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct mtk_desc_function *mtk_pctrl_find_function_by_pin(
|
||||
struct mtk_pinctrl *pctl, u32 pin_num, u32 fnum)
|
||||
{
|
||||
const struct mtk_desc_pin *pin = pctl->devdata->pins + pin_num;
|
||||
const struct mtk_desc_function *func = pin->functions;
|
||||
|
||||
while (func && func->name) {
|
||||
if (func->muxval == fnum)
|
||||
return func;
|
||||
func++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool mtk_pctrl_is_function_valid(struct mtk_pinctrl *pctl,
|
||||
u32 pin_num, u32 fnum)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pctl->devdata->npins; i++) {
|
||||
const struct mtk_desc_pin *pin = pctl->devdata->pins + i;
|
||||
|
||||
if (pin->pin.number == pin_num) {
|
||||
const struct mtk_desc_function *func =
|
||||
pin->functions;
|
||||
|
||||
while (func && func->name) {
|
||||
if (func->muxval == fnum)
|
||||
return true;
|
||||
func++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mtk_pctrl_dt_node_to_map_func(struct mtk_pinctrl *pctl,
|
||||
u32 pin, u32 fnum, struct mtk_pinctrl_group *grp,
|
||||
struct pinctrl_map **map, unsigned *reserved_maps,
|
||||
unsigned *num_maps)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (*num_maps == *reserved_maps)
|
||||
return -ENOSPC;
|
||||
|
||||
(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
|
||||
(*map)[*num_maps].data.mux.group = grp->name;
|
||||
|
||||
ret = mtk_pctrl_is_function_valid(pctl, pin, fnum);
|
||||
if (!ret) {
|
||||
dev_err(pctl->dev, "invalid function %d on pin %d .\n",
|
||||
fnum, pin);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
(*map)[*num_maps].data.mux.function = mtk_gpio_functions[fnum];
|
||||
(*num_maps)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
|
||||
struct device_node *node,
|
||||
struct pinctrl_map **map,
|
||||
unsigned *reserved_maps,
|
||||
unsigned *num_maps)
|
||||
{
|
||||
struct property *pins;
|
||||
u32 pinfunc, pin, func;
|
||||
int num_pins, num_funcs, maps_per_pin;
|
||||
unsigned long *configs;
|
||||
unsigned int num_configs;
|
||||
bool has_config = 0;
|
||||
int i, err;
|
||||
unsigned reserve = 0;
|
||||
struct mtk_pinctrl_group *grp;
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
pins = of_find_property(node, "pinmux", NULL);
|
||||
if (!pins) {
|
||||
dev_err(pctl->dev, "missing pins property in node %s .\n",
|
||||
node->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = pinconf_generic_parse_dt_config(node, &configs, &num_configs);
|
||||
if (num_configs)
|
||||
has_config = 1;
|
||||
|
||||
num_pins = pins->length / sizeof(u32);
|
||||
num_funcs = num_pins;
|
||||
maps_per_pin = 0;
|
||||
if (num_funcs)
|
||||
maps_per_pin++;
|
||||
if (has_config && num_pins >= 1)
|
||||
maps_per_pin++;
|
||||
|
||||
if (!num_pins || !maps_per_pin)
|
||||
return -EINVAL;
|
||||
|
||||
reserve = num_pins * maps_per_pin;
|
||||
|
||||
err = pinctrl_utils_reserve_map(pctldev, map,
|
||||
reserved_maps, num_maps, reserve);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < num_pins; i++) {
|
||||
err = of_property_read_u32_index(node, "pinmux",
|
||||
i, &pinfunc);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
pin = MTK_GET_PIN_NO(pinfunc);
|
||||
func = MTK_GET_PIN_FUNC(pinfunc);
|
||||
|
||||
if (pin >= pctl->devdata->npins ||
|
||||
func >= ARRAY_SIZE(mtk_gpio_functions)) {
|
||||
dev_err(pctl->dev, "invalid pins value.\n");
|
||||
err = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
grp = mtk_pctrl_find_group_by_pin(pctl, pin);
|
||||
if (!grp) {
|
||||
dev_err(pctl->dev, "unable to match pin %d to group\n",
|
||||
pin);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = mtk_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map,
|
||||
reserved_maps, num_maps);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
if (has_config) {
|
||||
err = pinctrl_utils_add_map_configs(pctldev, map,
|
||||
reserved_maps, num_maps, grp->name,
|
||||
configs, num_configs,
|
||||
PIN_MAP_TYPE_CONFIGS_GROUP);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mtk_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
|
||||
struct device_node *np_config,
|
||||
struct pinctrl_map **map, unsigned *num_maps)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned reserved_maps;
|
||||
int ret;
|
||||
|
||||
*map = NULL;
|
||||
*num_maps = 0;
|
||||
reserved_maps = 0;
|
||||
|
||||
for_each_child_of_node(np_config, np) {
|
||||
ret = mtk_pctrl_dt_subnode_to_map(pctldev, np, map,
|
||||
&reserved_maps, num_maps);
|
||||
if (ret < 0) {
|
||||
pinctrl_utils_dt_free_map(pctldev, *map, *num_maps);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
return pctl->ngroups;
|
||||
}
|
||||
|
||||
static const char *mtk_pctrl_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned group)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
return pctl->groups[group].name;
|
||||
}
|
||||
|
||||
static int mtk_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
|
||||
unsigned group,
|
||||
const unsigned **pins,
|
||||
unsigned *num_pins)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
*pins = (unsigned *)&pctl->groups[group].pin;
|
||||
*num_pins = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinctrl_ops mtk_pctrl_ops = {
|
||||
.dt_node_to_map = mtk_pctrl_dt_node_to_map,
|
||||
.dt_free_map = pinctrl_utils_dt_free_map,
|
||||
.get_groups_count = mtk_pctrl_get_groups_count,
|
||||
.get_group_name = mtk_pctrl_get_group_name,
|
||||
.get_group_pins = mtk_pctrl_get_group_pins,
|
||||
};
|
||||
|
||||
static int mtk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
return ARRAY_SIZE(mtk_gpio_functions);
|
||||
}
|
||||
|
||||
static const char *mtk_pmx_get_func_name(struct pinctrl_dev *pctldev,
|
||||
unsigned selector)
|
||||
{
|
||||
return mtk_gpio_functions[selector];
|
||||
}
|
||||
|
||||
static int mtk_pmx_get_func_groups(struct pinctrl_dev *pctldev,
|
||||
unsigned function,
|
||||
const char * const **groups,
|
||||
unsigned * const num_groups)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
*groups = pctl->grp_names;
|
||||
*num_groups = pctl->ngroups;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pmx_set_mode(struct pinctrl_dev *pctldev,
|
||||
unsigned long pin, unsigned long mode)
|
||||
{
|
||||
unsigned int reg_addr;
|
||||
unsigned char bit;
|
||||
unsigned int val;
|
||||
unsigned int mask = (1L << GPIO_MODE_BITS) - 1;
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
reg_addr = ((pin / MAX_GPIO_MODE_PER_REG) << pctl->devdata->port_shf)
|
||||
+ pctl->devdata->pinmux_offset;
|
||||
|
||||
bit = pin % MAX_GPIO_MODE_PER_REG;
|
||||
mask <<= (GPIO_MODE_BITS * bit);
|
||||
val = (mode << (GPIO_MODE_BITS * bit));
|
||||
return regmap_update_bits(mtk_get_regmap(pctl, pin),
|
||||
reg_addr, mask, val);
|
||||
}
|
||||
|
||||
static int mtk_pmx_set_mux(struct pinctrl_dev *pctldev,
|
||||
unsigned function,
|
||||
unsigned group)
|
||||
{
|
||||
bool ret;
|
||||
const struct mtk_desc_function *desc;
|
||||
struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||||
struct mtk_pinctrl_group *g = pctl->groups + group;
|
||||
|
||||
ret = mtk_pctrl_is_function_valid(pctl, g->pin, function);
|
||||
if (!ret) {
|
||||
dev_err(pctl->dev, "invaild function %d on group %d .\n",
|
||||
function, group);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc = mtk_pctrl_find_function_by_pin(pctl, g->pin, function);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
mtk_pmx_set_mode(pctldev, g->pin, desc->muxval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinmux_ops mtk_pmx_ops = {
|
||||
.get_functions_count = mtk_pmx_get_funcs_cnt,
|
||||
.get_function_name = mtk_pmx_get_func_name,
|
||||
.get_function_groups = mtk_pmx_get_func_groups,
|
||||
.set_mux = mtk_pmx_set_mux,
|
||||
.gpio_set_direction = mtk_pmx_gpio_set_direction,
|
||||
};
|
||||
|
||||
static int mtk_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return pinctrl_request_gpio(chip->base + offset);
|
||||
}
|
||||
|
||||
static void mtk_gpio_free(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
pinctrl_free_gpio(chip->base + offset);
|
||||
}
|
||||
|
||||
static int mtk_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
return pinctrl_gpio_direction_input(chip->base + offset);
|
||||
}
|
||||
|
||||
static int mtk_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
mtk_gpio_set(chip, offset, value);
|
||||
return pinctrl_gpio_direction_output(chip->base + offset);
|
||||
}
|
||||
|
||||
static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
unsigned int reg_addr;
|
||||
unsigned int bit;
|
||||
unsigned int read_val = 0;
|
||||
|
||||
struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev);
|
||||
|
||||
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
|
||||
bit = BIT(offset & 0xf);
|
||||
regmap_read(pctl->regmap1, reg_addr, &read_val);
|
||||
return !!(read_val & bit);
|
||||
}
|
||||
|
||||
static int mtk_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
unsigned int reg_addr;
|
||||
unsigned int bit;
|
||||
unsigned int read_val = 0;
|
||||
struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev);
|
||||
|
||||
if (mtk_gpio_get_direction(chip, offset))
|
||||
reg_addr = mtk_get_port(pctl, offset) +
|
||||
pctl->devdata->dout_offset;
|
||||
else
|
||||
reg_addr = mtk_get_port(pctl, offset) +
|
||||
pctl->devdata->din_offset;
|
||||
|
||||
bit = BIT(offset & 0xf);
|
||||
regmap_read(pctl->regmap1, reg_addr, &read_val);
|
||||
return !!(read_val & bit);
|
||||
}
|
||||
|
||||
static struct gpio_chip mtk_gpio_chip = {
|
||||
.owner = THIS_MODULE,
|
||||
.request = mtk_gpio_request,
|
||||
.free = mtk_gpio_free,
|
||||
.direction_input = mtk_gpio_direction_input,
|
||||
.direction_output = mtk_gpio_direction_output,
|
||||
.get = mtk_gpio_get,
|
||||
.set = mtk_gpio_set,
|
||||
.of_gpio_n_cells = 2,
|
||||
};
|
||||
|
||||
static int mtk_pctrl_build_state(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_pinctrl *pctl = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
pctl->ngroups = pctl->devdata->npins;
|
||||
|
||||
/* Allocate groups */
|
||||
pctl->groups = devm_kzalloc(&pdev->dev,
|
||||
pctl->ngroups * sizeof(*pctl->groups),
|
||||
GFP_KERNEL);
|
||||
if (!pctl->groups)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We assume that one pin is one group, use pin name as group name. */
|
||||
pctl->grp_names = devm_kzalloc(&pdev->dev,
|
||||
pctl->ngroups * sizeof(*pctl->grp_names),
|
||||
GFP_KERNEL);
|
||||
if (!pctl->grp_names)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pctl->devdata->npins; i++) {
|
||||
const struct mtk_desc_pin *pin = pctl->devdata->pins + i;
|
||||
struct mtk_pinctrl_group *group = pctl->groups + i;
|
||||
|
||||
group->name = pin->pin.name;
|
||||
group->pin = pin->pin.number;
|
||||
|
||||
pctl->grp_names[i] = pin->pin.name;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pinctrl_desc mtk_pctrl_desc = {
|
||||
.confops = &mtk_pconf_ops,
|
||||
.pctlops = &mtk_pctrl_ops,
|
||||
.pmxops = &mtk_pmx_ops,
|
||||
};
|
||||
|
||||
int mtk_pctrl_init(struct platform_device *pdev,
|
||||
const struct mtk_pinctrl_devdata *data)
|
||||
{
|
||||
struct pinctrl_pin_desc *pins;
|
||||
struct mtk_pinctrl *pctl;
|
||||
struct device_node *np = pdev->dev.of_node, *node;
|
||||
struct property *prop;
|
||||
int i, ret;
|
||||
|
||||
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
|
||||
if (!pctl)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, pctl);
|
||||
|
||||
prop = of_find_property(np, "pins-are-numbered", NULL);
|
||||
if (!prop) {
|
||||
dev_err(&pdev->dev, "only support pins-are-numbered format\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
node = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
|
||||
if (node) {
|
||||
pctl->regmap1 = syscon_node_to_regmap(node);
|
||||
if (IS_ERR(pctl->regmap1))
|
||||
return PTR_ERR(pctl->regmap1);
|
||||
}
|
||||
|
||||
/* Only 8135 has two base addr, other SoCs have only one. */
|
||||
node = of_parse_phandle(np, "mediatek,pctl-regmap", 1);
|
||||
if (node) {
|
||||
pctl->regmap2 = syscon_node_to_regmap(node);
|
||||
if (IS_ERR(pctl->regmap2))
|
||||
return PTR_ERR(pctl->regmap2);
|
||||
}
|
||||
|
||||
pctl->devdata = data;
|
||||
ret = mtk_pctrl_build_state(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "build state failed: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pins = devm_kzalloc(&pdev->dev,
|
||||
pctl->devdata->npins * sizeof(*pins),
|
||||
GFP_KERNEL);
|
||||
if (!pins)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pctl->devdata->npins; i++)
|
||||
pins[i] = pctl->devdata->pins[i].pin;
|
||||
mtk_pctrl_desc.name = dev_name(&pdev->dev);
|
||||
mtk_pctrl_desc.owner = THIS_MODULE;
|
||||
mtk_pctrl_desc.pins = pins;
|
||||
mtk_pctrl_desc.npins = pctl->devdata->npins;
|
||||
pctl->dev = &pdev->dev;
|
||||
pctl->pctl_dev = pinctrl_register(&mtk_pctrl_desc, &pdev->dev, pctl);
|
||||
if (!pctl->pctl_dev) {
|
||||
dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);
|
||||
if (!pctl->chip) {
|
||||
ret = -ENOMEM;
|
||||
goto pctrl_error;
|
||||
}
|
||||
|
||||
pctl->chip = &mtk_gpio_chip;
|
||||
pctl->chip->ngpio = pctl->devdata->npins;
|
||||
pctl->chip->label = dev_name(&pdev->dev);
|
||||
pctl->chip->dev = &pdev->dev;
|
||||
pctl->chip->base = 0;
|
||||
|
||||
ret = gpiochip_add(pctl->chip);
|
||||
if (ret) {
|
||||
ret = -EINVAL;
|
||||
goto pctrl_error;
|
||||
}
|
||||
|
||||
/* Register the GPIO to pin mappings. */
|
||||
ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev),
|
||||
0, 0, pctl->devdata->npins);
|
||||
if (ret) {
|
||||
ret = -EINVAL;
|
||||
goto chip_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
chip_error:
|
||||
gpiochip_remove(pctl->chip);
|
||||
pctrl_error:
|
||||
pinctrl_unregister(pctl->pctl_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MediaTek Pinctrl Driver");
|
||||
MODULE_AUTHOR("Hongzhou Yang <hongzhou.yang@mediatek.com>");
|
185
drivers/pinctrl/mediatek/pinctrl-mtk-common.h
Normal file
185
drivers/pinctrl/mediatek/pinctrl-mtk-common.h
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: Hongzhou.Yang <hongzhou.yang@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __PINCTRL_MTK_COMMON_H
|
||||
#define __PINCTRL_MTK_COMMON_H
|
||||
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define NO_EINT_SUPPORT 255
|
||||
|
||||
struct mtk_desc_function {
|
||||
const char *name;
|
||||
unsigned char muxval;
|
||||
};
|
||||
|
||||
struct mtk_desc_eint {
|
||||
unsigned char eintmux;
|
||||
unsigned char eintnum;
|
||||
};
|
||||
|
||||
struct mtk_desc_pin {
|
||||
struct pinctrl_pin_desc pin;
|
||||
const char *chip;
|
||||
const struct mtk_desc_eint eint;
|
||||
const struct mtk_desc_function *functions;
|
||||
};
|
||||
|
||||
#define MTK_PIN(_pin, _pad, _chip, _eint, ...) \
|
||||
{ \
|
||||
.pin = _pin, \
|
||||
.chip = _chip, \
|
||||
.eint = _eint, \
|
||||
.functions = (struct mtk_desc_function[]){ \
|
||||
__VA_ARGS__, { } }, \
|
||||
}
|
||||
|
||||
#define MTK_EINT_FUNCTION(_eintmux, _eintnum) \
|
||||
{ \
|
||||
.eintmux = _eintmux, \
|
||||
.eintnum = _eintnum, \
|
||||
}
|
||||
|
||||
#define MTK_FUNCTION(_val, _name) \
|
||||
{ \
|
||||
.muxval = _val, \
|
||||
.name = _name, \
|
||||
}
|
||||
|
||||
#define SET_ADDR(x, y) (x + (y->devdata->port_align))
|
||||
#define CLR_ADDR(x, y) (x + (y->devdata->port_align << 1))
|
||||
|
||||
struct mtk_pinctrl_group {
|
||||
const char *name;
|
||||
unsigned long config;
|
||||
unsigned pin;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtk_drv_group_desc - Provide driving group data.
|
||||
* @max_drv: The maximum current of this group.
|
||||
* @min_drv: The minimum current of this group.
|
||||
* @low_bit: The lowest bit of this group.
|
||||
* @high_bit: The highest bit of this group.
|
||||
* @step: The step current of this group.
|
||||
*/
|
||||
struct mtk_drv_group_desc {
|
||||
unsigned char min_drv;
|
||||
unsigned char max_drv;
|
||||
unsigned char low_bit;
|
||||
unsigned char high_bit;
|
||||
unsigned char step;
|
||||
};
|
||||
|
||||
#define MTK_DRV_GRP(_min, _max, _low, _high, _step) \
|
||||
{ \
|
||||
.min_drv = _min, \
|
||||
.max_drv = _max, \
|
||||
.low_bit = _low, \
|
||||
.high_bit = _high, \
|
||||
.step = _step, \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct mtk_pin_drv_grp - Provide each pin driving info.
|
||||
* @pin: The pin number.
|
||||
* @offset: The offset of driving register for this pin.
|
||||
* @bit: The bit of driving register for this pin.
|
||||
* @grp: The group for this pin belongs to.
|
||||
*/
|
||||
struct mtk_pin_drv_grp {
|
||||
unsigned int pin;
|
||||
unsigned int offset;
|
||||
unsigned char bit;
|
||||
unsigned char grp;
|
||||
};
|
||||
|
||||
#define MTK_PIN_DRV_GRP(_pin, _offset, _bit, _grp) \
|
||||
{ \
|
||||
.pin = _pin, \
|
||||
.offset = _offset, \
|
||||
.bit = _bit, \
|
||||
.grp = _grp, \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct mtk_pinctrl_devdata - Provide HW GPIO related data.
|
||||
* @pins: An array describing all pins the pin controller affects.
|
||||
* @npins: The number of entries in @pins.
|
||||
*
|
||||
* @grp_desc: The driving group info.
|
||||
* @pin_drv_grp: The driving group for all pins.
|
||||
* @spec_pull_set: Each SoC may have special pins for pull up/down setting,
|
||||
* these pins' pull setting are very different, they have separate pull
|
||||
* up/down bit, R0 and R1 resistor bit, so they need special pull setting.
|
||||
* If special setting is success, this should return 0, otherwise it should
|
||||
* return non-zero value.
|
||||
*
|
||||
* @dir_offset: The direction register offset.
|
||||
* @pullen_offset: The pull-up/pull-down enable register offset.
|
||||
* @pinmux_offset: The pinmux register offset.
|
||||
*
|
||||
* @type1_start: Some chips have two base addresses for pull select register,
|
||||
* that means some pins use the first address and others use the second. This
|
||||
* member record the start of pin number to use the second address.
|
||||
* @type1_end: The end of pin number to use the second address.
|
||||
*
|
||||
* @port_shf: The shift between two registers.
|
||||
* @port_mask: The mask of register.
|
||||
* @port_align: Provide clear register and set register step.
|
||||
*/
|
||||
struct mtk_pinctrl_devdata {
|
||||
const struct mtk_desc_pin *pins;
|
||||
unsigned int npins;
|
||||
const struct mtk_drv_group_desc *grp_desc;
|
||||
unsigned int n_grp_cls;
|
||||
const struct mtk_pin_drv_grp *pin_drv_grp;
|
||||
unsigned int n_pin_drv_grps;
|
||||
int (*spec_pull_set)(struct regmap *reg, unsigned int pin,
|
||||
unsigned char align, bool isup, unsigned int arg);
|
||||
unsigned int dir_offset;
|
||||
unsigned int ies_offset;
|
||||
unsigned int smt_offset;
|
||||
unsigned int pullen_offset;
|
||||
unsigned int pullsel_offset;
|
||||
unsigned int drv_offset;
|
||||
unsigned int invser_offset;
|
||||
unsigned int dout_offset;
|
||||
unsigned int din_offset;
|
||||
unsigned int pinmux_offset;
|
||||
unsigned short type1_start;
|
||||
unsigned short type1_end;
|
||||
unsigned char port_shf;
|
||||
unsigned char port_mask;
|
||||
unsigned char port_align;
|
||||
};
|
||||
|
||||
struct mtk_pinctrl {
|
||||
struct regmap *regmap1;
|
||||
struct regmap *regmap2;
|
||||
struct device *dev;
|
||||
struct gpio_chip *chip;
|
||||
struct mtk_pinctrl_group *groups;
|
||||
unsigned ngroups;
|
||||
const char **grp_names;
|
||||
struct pinctrl_dev *pctl_dev;
|
||||
const struct mtk_pinctrl_devdata *devdata;
|
||||
};
|
||||
|
||||
int mtk_pctrl_init(struct platform_device *pdev,
|
||||
const struct mtk_pinctrl_devdata *data);
|
||||
|
||||
#endif /* __PINCTRL_MTK_COMMON_H */
|
2114
drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h
Normal file
2114
drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h
Normal file
File diff suppressed because it is too large
Load Diff
40
include/dt-bindings/pinctrl/mt65xx.h
Normal file
40
include/dt-bindings/pinctrl/mt65xx.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: Hongzhou.Yang <hongzhou.yang@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_PINCTRL_MT65XX_H
|
||||
#define _DT_BINDINGS_PINCTRL_MT65XX_H
|
||||
|
||||
#define MTK_PIN_NO(x) ((x) << 8)
|
||||
#define MTK_GET_PIN_NO(x) ((x) >> 8)
|
||||
#define MTK_GET_PIN_FUNC(x) ((x) & 0xf)
|
||||
|
||||
#define MTK_PUPD_SET_R1R0_00 100
|
||||
#define MTK_PUPD_SET_R1R0_01 101
|
||||
#define MTK_PUPD_SET_R1R0_10 102
|
||||
#define MTK_PUPD_SET_R1R0_11 103
|
||||
|
||||
#define MTK_DRIVE_2mA 2
|
||||
#define MTK_DRIVE_4mA 4
|
||||
#define MTK_DRIVE_6mA 6
|
||||
#define MTK_DRIVE_8mA 8
|
||||
#define MTK_DRIVE_10mA 10
|
||||
#define MTK_DRIVE_12mA 12
|
||||
#define MTK_DRIVE_14mA 14
|
||||
#define MTK_DRIVE_16mA 16
|
||||
#define MTK_DRIVE_20mA 20
|
||||
#define MTK_DRIVE_24mA 24
|
||||
#define MTK_DRIVE_28mA 28
|
||||
#define MTK_DRIVE_32mA 32
|
||||
|
||||
#endif /* _DT_BINDINGS_PINCTRL_MT65XX_H */
|
Loading…
Reference in New Issue
Block a user