meta-ox64-bsp/recipes-kernel/linux/linux-yocto/0028-Add-GPIO-PINCTRL-and-HWRNG-Crypto-drivers.patch
Grzegorz Kowalski 3e13a0fa03 add initial bootable image
based on openbouffalo/meta-bl808
2024-08-31 17:45:25 +02:00

1508 lines
46 KiB
Diff

From 6adabb3a26a433cc80d906eb42908c558a119665 Mon Sep 17 00:00:00 2001
From: Alexander Horner <33007665+alexhorner@users.noreply.github.com>
Date: Tue, 14 Feb 2023 22:44:18 +0000
Subject: [PATCH 28/31] Add GPIO/PINCTRL and HWRNG/Crypto drivers
---
drivers/crypto/Kconfig | 11 +
drivers/crypto/Makefile | 1 +
drivers/crypto/bflb-seceng.c | 670 +++++++++++++++++++++++
drivers/pinctrl/Kconfig | 16 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-bflb.c | 719 +++++++++++++++++++++++++
include/dt-bindings/mailbox/bflb-ipc.h | 3 +-
7 files changed, 1420 insertions(+), 1 deletion(-)
create mode 100644 drivers/crypto/bflb-seceng.c
create mode 100644 drivers/pinctrl/pinctrl-bflb.c
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index dfb103f81a64..e8de3d60ffd8 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -13,6 +13,17 @@ if CRYPTO_HW
source "drivers/crypto/allwinner/Kconfig"
+config CRYPTO_DEV_BFLB_SECENG
+ tristate "Bouffalo Lab Secure Engine Driver"
+ depends on SOC_BOUFFALOLAB
+ select CRYPTO_RNG
+ help
+ This driver provides support for the Random Number
+ Generator hardware found on Bouffalo Lab BL808 SoCs.
+
+ To compile this driver as a module, choose M here. The
+ module will be called bflb-seceng. If unsure, say N.
+
config CRYPTO_DEV_PADLOCK
tristate "Support for VIA PadLock ACE"
depends on X86 && !UML
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index fa8bf1be1a8c..1b5c60587c43 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o
obj-$(CONFIG_CRYPTO_DEV_ATMEL_I2C) += atmel-i2c.o
obj-$(CONFIG_CRYPTO_DEV_ATMEL_ECC) += atmel-ecc.o
obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA204A) += atmel-sha204a.o
+obj-$(CONFIG_CRYPTO_DEV_BFLB_SECENG) += bflb-seceng.o
obj-$(CONFIG_CRYPTO_DEV_CAVIUM_ZIP) += cavium/
obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/
obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
diff --git a/drivers/crypto/bflb-seceng.c b/drivers/crypto/bflb-seceng.c
new file mode 100644
index 000000000000..3e450f06ddea
--- /dev/null
+++ b/drivers/crypto/bflb-seceng.c
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Bouffalo Lab SoC Secure Engine driver
+//
+// Based on qcom-rng.c
+// Copyright (c) 2017-18 Linaro Limited
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <crypto/internal/rng.h>
+#include <linux/crypto.h>
+#include <linux/hw_random.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+
+//Register map
+//se_sha_0_ctrl
+#define REG_SECENG_SHA_0_CTRL 0 //Offset from base address
+#define REG_SECENG_SHA_0_CTRL_MSG_LEN GENMASK(31, 16)
+#define REG_SECENG_SHA_0_CTRL_LINK_MODE BIT(15)
+#define REG_SECENG_SHA_0_CTRL_MODE_EXT GENMASK(13, 12)
+#define REG_SECENG_SHA_0_CTRL_INT_MASK BIT(11)
+#define REG_SECENG_SHA_0_CTRL_INT_SET_1T BIT(10)
+#define REG_SECENG_SHA_0_CTRL_INT_CLR_1T BIT(9)
+#define REG_SECENG_SHA_0_CTRL_INT BIT(8)
+#define REG_SECENG_SHA_0_CTRL_HASH_SEL BIT(6)
+#define REG_SECENG_SHA_0_CTRL_EN BIT(5)
+#define REG_SECENG_SHA_0_CTRL_MODE GENMASK(4, 2)
+#define REG_SECENG_SHA_0_CTRL_INT_TRIG_1T BIT(1)
+#define REG_SECENG_SHA_0_CTRL_BUSY BIT(0)
+
+//se_sha_0_msa
+#define REG_SECENG_SHA_0_MSA 4
+
+//se_sha_0_status
+#define REG_SECENG_SHA_0_STATUS 8
+
+//se_sha_0_endian
+#define REG_SECENG_SHA_0_ENDIAN 12
+#define REG_SECENG_SHA_0_ENDIAN_VAL BIT(0)
+
+//se_sha_0_hash_l_0
+#define REG_SECENG_SHA_0_HASH_L_0 16
+
+//se_sha_0_hash_l_1
+#define REG_SECENG_SHA_0_HASH_L_1 20
+
+//se_sha_0_hash_l_2
+#define REG_SECENG_SHA_0_HASH_L_2 24
+
+//se_sha_0_hash_l_3
+#define REG_SECENG_SHA_0_HASH_L_3 28
+
+//se_sha_0_hash_l_4
+#define REG_SECENG_SHA_0_HASH_L_4 32
+
+//se_sha_0_hash_l_5
+#define REG_SECENG_SHA_0_HASH_L_5 36
+
+//se_sha_0_hash_l_6
+#define REG_SECENG_SHA_0_HASH_L_6 40
+
+//se_sha_0_hash_l_7
+#define REG_SECENG_SHA_0_HASH_L_7 44
+
+//se_sha_0_hash_h_0
+#define REG_SECENG_SHA_0_HASH_H_0 48
+
+//se_sha_0_hash_h_1
+#define REG_SECENG_SHA_0_HASH_H_1 52
+
+//se_sha_0_hash_h_2
+#define REG_SECENG_SHA_0_HASH_H_2 56
+
+//se_sha_0_hash_h_3
+#define REG_SECENG_SHA_0_HASH_H_3 60
+
+//se_sha_0_hash_h_4
+#define REG_SECENG_SHA_0_HASH_H_4 64
+
+//se_sha_0_hash_h_5
+#define REG_SECENG_SHA_0_HASH_H_5 68
+
+//se_sha_0_hash_h_6
+#define REG_SECENG_SHA_0_HASH_H_6 72
+
+//se_sha_0_hash_h_7
+#define REG_SECENG_SHA_0_HASH_H_7 76
+
+//se_sha_0_link
+#define REG_SECENG_SHA_0_LINK 80
+
+//se_sha_0_ctrl_prot
+#define REG_SECENG_SHA_0_CTRL_PROT 252
+#define REG_SECENG_SHA_0_CTRL_PROT_ID1_EN BIT(2)
+#define REG_SECENG_SHA_0_CTRL_PROT_ID0_EN BIT(1)
+
+//se_aes_0_ctrl
+#define REG_SECENG_AES_0_CTRL 256
+#define REG_SECENG_AES_0_CTRL_MSG_LEN GENMASK(31, 16)
+#define REG_SECENG_AES_0_CTRL_LINK_MODE BIT(15)
+#define REG_SECENG_AES_0_CTRL_IV_SEL BIT(14)
+#define REG_SECENG_AES_0_CTRL_BLOCK_MODE GENMASK(13, 12)
+#define REG_SECENG_AES_0_CTRL_INT_MASK BIT(11)
+#define REG_SECENG_AES_0_CTRL_INT_SET_1T BIT(10)
+#define REG_SECENG_AES_0_CTRL_INT_CLR_1T BIT(9)
+#define REG_SECENG_AES_0_CTRL_INT BIT(8)
+#define REG_SECENG_AES_0_CTRL_HW_KEY_EN BIT(7)
+#define REG_SECENG_AES_0_CTRL_DEC_KEY_SEL BIT(6)
+#define REG_SECENG_AES_0_CTRL_DEC_EN BIT(5)
+#define REG_SECENG_AES_0_CTRL_MODE GENMASK(4, 3)
+#define REG_SECENG_AES_0_CTRL_EN BIT(2)
+#define REG_SECENG_AES_0_CTRL_TRIG_1T BIT(1)
+#define REG_SECENG_AES_0_CTRL_BUSY BIT(0)
+
+//se_aes_0_msa
+#define REG_SECENG_AES_0_MSA 260
+
+//se_aes_0_mda
+#define REG_SECENG_AES_0_MDA 264
+
+//se_aes_0_status
+#define REG_SECENG_AES_0_STATUS 268
+
+//se_aes_0_iv_0
+#define REG_SECENG_AES_0_IV_0 272
+
+//se_aes_0_iv_1
+#define REG_SECENG_AES_0_IV_1 276
+
+//se_aes_0_iv_2
+#define REG_SECENG_AES_0_IV_2 280
+
+//se_aes_0_iv_3
+#define REG_SECENG_AES_0_IV_3 284
+
+//se_aes_0_key_0
+#define REG_SECENG_AES_0_KEY_0 288
+
+//se_aes_0_key_1
+#define REG_SECENG_AES_0_KEY_1 292
+
+//se_aes_0_key_2
+#define REG_SECENG_AES_0_KEY_2 296
+
+//se_aes_0_key_3
+#define REG_SECENG_AES_0_KEY_3 300
+
+//se_aes_0_key_4
+#define REG_SECENG_AES_0_KEY_4 304
+
+//se_aes_0_key_5
+#define REG_SECENG_AES_0_KEY_5 308
+
+//se_aes_0_key_6
+#define REG_SECENG_AES_0_KEY_6 312
+
+//se_aes_0_key_7
+#define REG_SECENG_AES_0_KEY_7 316
+
+//se_aes_0_key_sel
+#define REG_SECENG_AES_0_KEY_SEL 320
+#define REG_SECENG_AES_0_KEY_SEL_VAL GENMASK(1, 0)
+
+//se_aes_1_key_sel
+#define REG_SECENG_AES_1_KEY_SEL 324
+#define REG_SECENG_AES_01KEY_SEL_VAL GENMASK(1, 0)
+
+//se_aes_0_endian
+#define REG_SECENG_AES_0_ENDIAN 328
+#define REG_SECENG_AES_0_ENDIAN_CTR_LEN GENMASK(31, 30)
+#define REG_SECENG_AES_0_ENDIAN_TWK BIT(4)
+#define REG_SECENG_AES_0_ENDIAN_IV BIT(3)
+#define REG_SECENG_AES_0_ENDIAN_KEY BIT(2)
+#define REG_SECENG_AES_0_ENDIAN_DIN BIT(1)
+#define REG_SECENG_AES_0_ENDIAN_DOUT BIT(0)
+
+//se_aes_sboot
+#define REG_SECENG_AES_SBOOT 332
+#define REG_SECENG_AES_SBOOT_UNI_LEN GENMASK(31, 16)
+#define REG_SECENG_AES_SBOOT_XTS_MODE BIT(15)
+#define REG_SECENG_AES_SBOOT_KEY_SEL BIT(0)
+
+//se_aes_0_link
+#define REG_SECENG_AES_0_LINK 336
+
+//se_aes_0_ctrl_prot
+#define REG_SECENG_AES_0_CTRL_PROT 508
+#define REG_SECENG_AES_0_CTRL_PROT_ID1_EN BIT(2)
+#define REG_SECENG_AES_0_CTRL_PROT_ID0_EN BIT(1)
+
+//se_trng_0_ctrl_0
+#define REG_SECENG_TRNG_0_CTRL_0 512
+#define REG_SECENG_TRNG_0_CTRL_0_MANUAL_EN BIT(15)
+#define REG_SECENG_TRNG_0_CTRL_0_MANUAL_RESEED BIT(14)
+#define REG_SECENG_TRNG_0_CTRL_0_MANUAL_FUN_SEL BIT(13)
+#define REG_SECENG_TRNG_0_CTRL_0_INT_MASK BIT(11)
+#define REG_SECENG_TRNG_0_CTRL_0_INT_SET_1T BIT(10)
+#define REG_SECENG_TRNG_0_CTRL_0_INT_CLR_1T BIT(9)
+#define REG_SECENG_TRNG_0_CTRL_0_INT BIT(8)
+#define REG_SECENG_TRNG_0_CTRL_0_HT_ERROR BIT(4)
+#define REG_SECENG_TRNG_0_CTRL_0_DOUT_CLR_1T BIT(3)
+#define REG_SECENG_TRNG_0_CTRL_0_EN BIT(2)
+#define REG_SECENG_TRNG_0_CTRL_0_TRIG_1T BIT(1)
+#define REG_SECENG_TRNG_0_CTRL_0_BUSY BIT(0)
+
+//se_trng_0_status
+#define REG_SECENG_TRNG_0_STATUS 516
+
+//se_trng_0_dout_0
+#define REG_SECENG_TRNG_0_DOUT_0 520
+
+//se_trng_0_dout_1
+#define REG_SECENG_TRNG_0_DOUT_1 524
+
+//se_trng_0_dout_2
+#define REG_SECENG_TRNG_0_DOUT_2 528
+
+//se_trng_0_dout_3
+#define REG_SECENG_TRNG_0_DOUT_3 532
+
+//se_trng_0_dout_4
+#define REG_SECENG_TRNG_0_DOUT_4 536
+
+//se_trng_0_dout_5
+#define REG_SECENG_TRNG_0_DOUT_5 540
+
+//se_trng_0_dout_6
+#define REG_SECENG_TRNG_0_DOUT_6 544
+
+//se_trng_0_dout_7
+#define REG_SECENG_TRNG_0_DOUT_7 548
+
+//se_trng_0_test
+#define REG_SECENG_TRNG_0_TEST 552
+#define REG_SECENG_TRNG_0_TEST_HT_ALARM_N GENMASK(11, 4)
+#define REG_SECENG_TRNG_0_TEST_HT_DIS BIT(3)
+#define REG_SECENG_TRNG_0_TEST_CP_BYPASS BIT(2)
+#define REG_SECENG_TRNG_0_TEST_CP_TEST_EN BIT(1)
+#define REG_SECENG_TRNG_0_TEST_TEST_EN BIT(0)
+
+//se_trng_0_ctrl_1
+#define REG_SECENG_TRNG_0_CTRL_1_RESEED_N_LSB 556
+
+//se_trng_0_ctrl_2
+#define REG_SECENG_TRNG_0_CTRL_2_RESEED_N_MSB 560
+#define REG_SECENG_TRNG_0_CTRL_1_RESEED_N_MSB_VALUE GENMASK(15, 0)
+
+//se_trng_0_ctrl_3
+#define REG_SECENG_TRNG_0_CTRL_3 564
+#define REG_SECENG_TRNG_0_CTRL_3_ROSC_EN BIT(31)
+#define REG_SECENG_TRNG_0_CTRL_3_HT_OD_EN BIT(26)
+#define REG_SECENG_TRNG_0_CTRL_3_HT_APT_C GENMASK(25, 16)
+#define REG_SECENG_TRNG_0_CTRL_3_HT_RCT_C GENMASK(15, 8)
+#define REG_SECENG_TRNG_0_CTRL_3_CP_RATIO GENMASK(7, 0)
+
+//se_trng_0_test_out_0
+#define REG_SECENG_TRNG_0_TEST_OUT_0 576
+
+//se_trng_0_test_out_1
+#define REG_SECENG_TRNG_0_TEST_OUT_1 580
+
+//se_trng_0_test_out_2
+#define REG_SECENG_TRNG_0_TEST_OUT_2 584
+
+//se_trng_0_test_out_3
+#define REG_SECENG_TRNG_0_TEST_OUT_3 588
+
+//se_trng_0_ctrl_prot
+#define REG_SECENG_TRNG_0_CTRL_PROT 764
+#define REG_SECENG_TRNG_0_CTRL_PROT_ID1_EN BIT(2)
+#define REG_SECENG_TRNG_0_CTRL_PROT_ID0_EN BIT(1)
+
+//se_pka_0_ctrl_0
+#define REG_SECENG_PKA_0_CTRL_0 768
+#define REG_SECENG_PKA_0_CTRL_0_STATUS GENMASK(31, 16)
+#define REG_SECENG_PKA_0_CTRL_0_STATUS_CLR_1T BIT(15)
+#define REG_SECENG_PKA_0_CTRL_0_RAM_CLR_MD BIT(13)
+#define REG_SECENG_PKA_0_CTRL_0_ENDIAN BIT(12)
+#define REG_SECENG_PKA_0_CTRL_0_INT_MASK BIT(11)
+#define REG_SECENG_PKA_0_CTRL_0_INT_SET BIT(10)
+#define REG_SECENG_PKA_0_CTRL_0_INT_CLR_1T BIT(9)
+#define REG_SECENG_PKA_0_CTRL_0_INT BIT(8)
+#define REG_SECENG_PKA_0_CTRL_0_PROT_MD GENMASK(7, 4)
+#define REG_SECENG_PKA_0_CTRL_0_EN BIT(3)
+#define REG_SECENG_PKA_0_CTRL_0_BUSY BIT(2)
+#define REG_SECENG_PKA_0_CTRL_0_DONE_CLR_1T BIT(1)
+#define REG_SECENG_PKA_0_CTRL_0_DONE BIT(0)
+
+//se_pka_0_seed
+#define REG_SECENG_PKA_0_SEED 780
+
+//se_pka_0_ctrl_1
+#define REG_SECENG_PKA_0_CTRL_1 784
+#define REG_SECENG_PKA_0_CTRL_1_HBYPASS BIT(3)
+#define REG_SECENG_PKA_0_CTRL_1_HBURST GENMASK(2, 0)
+
+//se_pka_0_rw
+#define REG_SECENG_PKA_0_RW 832
+//This is a confusing register which I have not mapped because
+//"BL808 Reference Manual 1.2 EN" seems wrong
+
+//se_pka_0_rw_burst
+#define REG_SECENG_PKA_0_RW_BURST 864
+//This is a confusing register which I have not mapped because
+//"BL808 Reference Manual 1.2 EN" seems wrong
+
+//se_pka_0_ctrl_prot
+#define REG_SECENG_PKA_0_CTRL_PROT 1020
+#define REG_SECENG_PKA_0_CTRL_PROT_ID1_EN BIT(2)
+#define REG_SECENG_PKA_0_CTRL_PROT_ID0_EN BIT(1)
+
+//se_cdet_0_ctrl_0
+#define REG_SECENG_CDET_0_CTRL_0 1024
+#define REG_SECENG_CDET_0_CTRL_0_G_LOOP_MIN GENMASK(31, 24)
+#define REG_SECENG_CDET_0_CTRL_0_G_LOOP_MAX GENMASK(23, 16)
+#define REG_SECENG_CDET_0_CTRL_0_STATUS GENMASK(15, 2)
+#define REG_SECENG_CDET_0_CTRL_0_ERROR BIT(1)
+#define REG_SECENG_CDET_0_CTRL_0_EN BIT(0)
+
+//se_cdet_0_ctrl_1
+#define REG_SECENG_CDET_0_CTRL_1 1028
+#define REG_SECENG_CDET_0_CTRL_1_G_SLP_N GENMASK(23, 16)
+#define REG_SECENG_CDET_0_CTRL_1_T_DLY_N GENMASK(15, 8)
+#define REG_SECENG_CDET_0_CTRL_1_T_LOOP_N GENMASK(7, 0)
+
+//se_cdet_0_ctrl_prot
+#define REG_SECENG_CDET_0_CTRL_PROT 1276
+#define REG_SECENG_CDET_0_CTRL_PROT_ID1_EN BIT(2)
+#define REG_SECENG_CDET_0_CTRL_PROT_ID0_EN BIT(1)
+#define REG_SECENG_CDET_0_CTRL_PROT_PROT_EN BIT(0)
+
+//se_gmac_0_ctrl_0
+#define REG_SECENG_GMAC_0_CTRL_0 1280
+#define REG_SECENG_GMAC_0_CTRL_0_X_ENDIAN BIT(14)
+#define REG_SECENG_GMAC_0_CTRL_0_H_ENDIAN BIT(13)
+#define REG_SECENG_GMAC_0_CTRL_0_T_ENDIAN BIT(12)
+#define REG_SECENG_GMAC_0_CTRL_0_INT_MASK BIT(11)
+#define REG_SECENG_GMAC_0_CTRL_0_INT_SET_1T BIT(10)
+#define REG_SECENG_GMAC_0_CTRL_0_INT_CLR_1T BIT(9)
+#define REG_SECENG_GMAC_0_CTRL_0_INT BIT(8)
+#define REG_SECENG_GMAC_0_CTRL_0_EN BIT(2)
+#define REG_SECENG_GMAC_0_CTRL_0_TRIG_1T BIT(1)
+#define REG_SECENG_GMAC_0_CTRL_0_BUSY BIT(0)
+
+//se_gmac_0_lca
+#define REG_SECENG_GMAC_0_LCA 1284
+
+//se_gmac_0_status
+#define REG_SECENG_GMAC_0_STATUS 1288
+
+//se_gmac_0_ctrl_prot
+#define REG_SECENG_GMAC_0_CTRL_PROT 1532
+#define REG_SECENG_GMAC_0_CTRL_PROT_ID1_EN BIT(2)
+#define REG_SECENG_GMAC_0_CTRL_PROT_ID0_EN BIT(1)
+
+//se_ctrl_prot_rd
+#define REG_SECENG_CTRL_PROT_RD 3840
+#define REG_SECENG_CTRL_PROT_RD_DBG_DIS BIT(31)
+#define REG_SECENG_CTRL_PROT_RD_GMAC_ID1_EN_RD BIT(11)
+#define REG_SECENG_CTRL_PROT_RD_GMAC_ID0_EN_RD BIT(10)
+#define REG_SECENG_CTRL_PROT_RD_CDET_ID1_EN_RD BIT(9)
+#define REG_SECENG_CTRL_PROT_RD_CDET_ID0_EN_RD BIT(8)
+#define REG_SECENG_CTRL_PROT_RD_PKA_ID1_EN_RD BIT(7)
+#define REG_SECENG_CTRL_PROT_RD_PKA_ID0_EN_RD BIT(6)
+#define REG_SECENG_CTRL_PROT_RD_TRNG_ID1_EN_RD BIT(5)
+#define REG_SECENG_CTRL_PROT_RD_TRNG_ID0_EN_RD BIT(4)
+#define REG_SECENG_CTRL_PROT_RD_AES_ID1_EN_RD BIT(3)
+#define REG_SECENG_CTRL_PROT_RD_AES_ID0_EN_RD BIT(2)
+#define REG_SECENG_CTRL_PROT_RD_SHA_ID1_EN_RD BIT(1)
+#define REG_SECENG_CTRL_PROT_RD_SHA_ID0_EN_RD BIT(0)
+
+struct bflb_seceng {
+ struct mutex lock;
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *map;
+ unsigned int initialised;
+
+ struct hwrng hwrng;
+};
+
+struct bflb_seceng_ctx {
+ struct bflb_seceng *seceng;
+};
+
+struct regmap_config bflb_seceng_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = 512 * sizeof(u32),
+ .num_reg_defaults_raw = 512,
+ .use_relaxed_mmio = true,
+ .use_raw_spinlock = true,
+};
+
+static struct bflb_seceng *bflb_seceng_dev;
+
+static void bflb_seceng_trng_wait_ready(void)
+{
+ while (readl_relaxed(bflb_seceng_dev->base + REG_SECENG_TRNG_0_CTRL_0) &
+ REG_SECENG_TRNG_0_CTRL_0_BUSY) {
+ dev_dbg(bflb_seceng_dev->dev, "Waiting for TRNG ready");
+ schedule();
+ }
+}
+
+static void bflb_seceng_trng_init(void)
+{
+ regmap_update_bits(bflb_seceng_dev->map, REG_SECENG_TRNG_0_CTRL_0,
+ REG_SECENG_TRNG_0_CTRL_0_EN | REG_SECENG_TRNG_0_CTRL_0_INT_CLR_1T,
+ FIELD_PREP(REG_SECENG_TRNG_0_CTRL_0_EN, 1) |
+ FIELD_PREP(REG_SECENG_TRNG_0_CTRL_0_INT_CLR_1T, 1));
+
+ bflb_seceng_trng_wait_ready();
+}
+
+static void bflb_seceng_trng_refresh(void)
+{
+ regmap_update_bits(bflb_seceng_dev->map, REG_SECENG_TRNG_0_CTRL_0,
+ REG_SECENG_TRNG_0_CTRL_0_DOUT_CLR_1T,
+ FIELD_PREP(REG_SECENG_TRNG_0_CTRL_0_DOUT_CLR_1T, 1)); //Clear DOUT
+
+ bflb_seceng_trng_wait_ready();
+
+ regmap_update_bits(bflb_seceng_dev->map, REG_SECENG_TRNG_0_CTRL_0,
+ REG_SECENG_TRNG_0_CTRL_0_DOUT_CLR_1T,
+ FIELD_PREP(REG_SECENG_TRNG_0_CTRL_0_DOUT_CLR_1T, 0)); //Reset clear DOUT
+
+ regmap_update_bits(bflb_seceng_dev->map,
+ REG_SECENG_TRNG_0_CTRL_0, REG_SECENG_TRNG_0_CTRL_0_TRIG_1T,
+ FIELD_PREP(REG_SECENG_TRNG_0_CTRL_0_TRIG_1T, 1)); //Force TRNG refresh
+
+ bflb_seceng_trng_wait_ready();
+
+ regmap_update_bits(bflb_seceng_dev->map, REG_SECENG_TRNG_0_CTRL_0,
+ REG_SECENG_TRNG_0_CTRL_0_INT_CLR_1T,
+ FIELD_PREP(REG_SECENG_TRNG_0_CTRL_0_INT_CLR_1T, 1)); //Clear INT
+
+ dev_dbg(bflb_seceng_dev->dev, "Refreshed TRNG");
+}
+
+static unsigned int bflb_seceng_trng_read_dout(struct bflb_seceng *seceng,
+ unsigned int doutreg)
+{
+ return readl_relaxed(seceng->base + REG_SECENG_TRNG_0_DOUT_0 +
+ (doutreg * 4));
+}
+
+static inline unsigned int bflb_seceng_trng_read32(void)
+{
+ static u8 doutreg = 8;
+ u32 val;
+
+ if (doutreg >= 8) {
+ //If we have read all available registers (of starting anew),
+ //refresh them and start again
+ doutreg = 0;
+
+ bflb_seceng_trng_refresh();
+ }
+
+ dev_dbg(bflb_seceng_dev->dev, "Selected TRNG DOUT register %u", doutreg);
+
+ //Read selected register
+ val = bflb_seceng_trng_read_dout(bflb_seceng_dev, doutreg);
+ doutreg++; //Move on to next register
+
+ dev_dbg(bflb_seceng_dev->dev, "TRNG DOUT register produced %u", val);
+
+ return val;
+}
+
+static inline u8 bflb_seceng_trng_read8(void)
+{
+ static unsigned int lastread;
+ static u8 shift = 4;
+
+ if (shift == 4) {
+ shift = 0;
+
+ lastread = bflb_seceng_trng_read32();
+ }
+
+ return (lastread >> (shift++ * 8)) & 0xFF;
+}
+
+static void bflb_seceng_trng_fill_buffer(u8 *buff, unsigned long bufflen)
+{
+ unsigned long i;
+
+ for (i = 0; i < bufflen; i++)
+ buff[i] = bflb_seceng_trng_read8();
+}
+
+static int bflb_seceng_trng_hwrng_read(struct hwrng *rng, void *data,
+ size_t max, bool wait)
+{
+ //Currently ignoring wait
+
+ mutex_lock(&bflb_seceng_dev->lock);
+
+ dev_dbg(bflb_seceng_dev->dev, "Starting TRNG hwrng read for %lu bytes...",
+ max);
+
+ bflb_seceng_trng_fill_buffer(data, max);
+
+ mutex_unlock(&bflb_seceng_dev->lock);
+
+ //We're always going to fill the buffer, so just return what was asked for
+ return max;
+}
+
+static int bflb_seceng_trng_crypto_generate(struct crypto_rng *tfm,
+ const u8 *src, unsigned int slen, u8 *dstn, unsigned int dlen)
+{
+ struct bflb_seceng_ctx *ctx = crypto_rng_ctx(tfm);
+ struct bflb_seceng *seceng = ctx->seceng;
+
+ mutex_lock(&seceng->lock);
+
+ dev_dbg(seceng->dev,
+ "Starting TRNG crypto buffer filling read for %u bytes...", dlen);
+
+ bflb_seceng_trng_fill_buffer(dstn, dlen);
+
+ mutex_unlock(&seceng->lock);
+
+ return 0;
+}
+
+static int bflb_seceng_trng_crypto_seed(struct crypto_rng *tfm, const u8 *seed,
+ unsigned int slen)
+{
+ return 0;
+}
+
+static int bflb_seceng_trng_crypto_init(struct crypto_tfm *tfm)
+{
+ struct bflb_seceng_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->seceng = bflb_seceng_dev;
+
+ //Skip actual initialisation of the hardware if we have already done so
+ if (bflb_seceng_dev->initialised)
+ return 0;
+
+ bflb_seceng_trng_init();
+
+ dev_dbg(bflb_seceng_dev->dev, "Initialised TRNG via crypto");
+
+ bflb_seceng_dev->initialised = 1;
+ return 0;
+}
+
+static int bflb_seceng_trng_hwrng_init(struct hwrng *rng)
+{
+ if (bflb_seceng_dev->initialised)
+ return 0;
+
+ bflb_seceng_trng_init();
+
+ dev_dbg(bflb_seceng_dev->dev, "Initialised TRNG via hwrng");
+
+ bflb_seceng_dev->initialised = 1;
+ return 0;
+}
+
+static struct rng_alg bflb_seceng_trng_alg = {
+ .generate = bflb_seceng_trng_crypto_generate,
+ .seed = bflb_seceng_trng_crypto_seed,
+ .seedsize = 0,
+ .base = {
+ .cra_name = "stdrng",
+ .cra_driver_name = "bflb-seceng",
+ .cra_flags = CRYPTO_ALG_TYPE_RNG,
+ .cra_priority = 300,
+ .cra_ctxsize = sizeof(struct bflb_seceng_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_init = bflb_seceng_trng_crypto_init,
+ }
+};
+
+static struct hwrng bflb_hwrng = {
+ .name = "bflb-seceng",
+ .init = bflb_seceng_trng_hwrng_init,
+ .read = bflb_seceng_trng_hwrng_read,
+};
+
+static int bflb_seceng_probe(struct platform_device *pdev)
+{
+ struct bflb_seceng *seceng;
+ int ret;
+
+ seceng = devm_kzalloc(&pdev->dev, sizeof(*seceng), GFP_KERNEL);
+
+ if (!seceng)
+ return -ENOMEM;
+
+ seceng->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, seceng);
+ mutex_init(&seceng->lock);
+
+ seceng->base = devm_platform_ioremap_resource(pdev, 0);
+
+ if (IS_ERR(seceng->base))
+ return PTR_ERR(seceng->base);
+
+ seceng->map = devm_regmap_init_mmio(&pdev->dev, seceng->base,
+ &bflb_seceng_regmap_config);
+
+ if (IS_ERR(seceng->map))
+ return dev_err_probe(&pdev->dev, PTR_ERR(seceng->map),
+ "Failed to create regmap\n");
+
+ bflb_seceng_dev = seceng; //Assign driver static
+
+ ret = crypto_register_rng(&bflb_seceng_trng_alg);
+
+ if (ret)
+ dev_err_probe(&pdev->dev, ret,
+ "Failed to register as a crypto random number generator\n");
+
+ seceng->hwrng = bflb_hwrng;
+
+ ret = hwrng_register(&seceng->hwrng);
+
+ if (ret)
+ dev_err_probe(&pdev->dev, ret,
+ "Failed to register as a hardware random number generator\n");
+
+ dev_info(&pdev->dev, "Bouffalo Lab Secure Engine");
+
+ return ret;
+}
+
+static int bflb_seceng_remove(struct platform_device *pdev)
+{
+ hwrng_unregister(&bflb_seceng_dev->hwrng);
+ crypto_unregister_rng(&bflb_seceng_trng_alg);
+
+ bflb_seceng_dev = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused bflb_seceng_of_match[] = {
+ { .compatible = "bflb,seceng", .data = (const void *)0, },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bflb_seceng_of_match);
+
+static struct platform_driver bflb_seceng_driver = {
+ .probe = bflb_seceng_probe,
+ .remove = bflb_seceng_remove,
+ .driver = {
+ .name = "bflb-seceng",
+ .of_match_table = bflb_seceng_of_match,
+ .suppress_bind_attrs = true,
+ }
+};
+module_platform_driver(bflb_seceng_driver);
+
+MODULE_DESCRIPTION("Bouffalo BL808 Secure Engine driver");
+MODULE_AUTHOR("Alexander Horner <contact@alexhorner.cc>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7d5f5458c72e..9120cd4b761f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -127,6 +127,22 @@ config PINCTRL_AXP209
selected.
Say Y to enable pinctrl and GPIO support for the AXP209 PMIC.
+config PINCTRL_BFLB_GPIO
+ tristate "Bouffalo Lab SoC GPIO pin controller driver"
+ depends on SOC_BOUFFALOLAB
+ select PINMUX
+ select GPIOLIB
+ select GPIOLIB_IRQCHIP
+ select GENERIC_PINCONF
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+ select OF_GPIO
+ help
+ This is the driver for the GPIO controller found on Bouffalo Lab RISC-V SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called pinctrl-bflb-gpio.
+
config PINCTRL_BM1880
bool "Bitmain BM1880 Pinctrl driver"
depends on OF && (ARCH_BITMAIN || COMPILE_TEST)
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index d5939840bb2a..9f7c89ed4bb4 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o
obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o
+obj-$(CONFIG_PINCTRL_BFLB_GPIO) += pinctrl-bflb.o
obj-$(CONFIG_PINCTRL_BM1880) += pinctrl-bm1880.o
obj-$(CONFIG_PINCTRL_CY8C95X0) += pinctrl-cy8c95x0.o
obj-$(CONFIG_PINCTRL_DA850_PUPD) += pinctrl-da850-pupd.o
diff --git a/drivers/pinctrl/pinctrl-bflb.c b/drivers/pinctrl/pinctrl-bflb.c
new file mode 100644
index 000000000000..4223fcc18ed5
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-bflb.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Bouffalo Lab SoC pinctrl+GPIO+external IRQ driver
+ *
+ * Based on: pinctrl-apple-gpio.c
+ * Copyright (C) The Asahi Linux Contributors
+ * Copyright (C) 2020 Corellium LLC
+ *
+ * Based on: pinctrl-pistachio.c
+ * Copyright (C) 2014 Imagination Technologies Ltd.
+ * Copyright (C) 2014 Google, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "pinctrl-utils.h"
+#include "core.h"
+#include "pinmux.h"
+
+struct bflb_gpio_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctldev;
+
+ void __iomem *base;
+ struct regmap *map;
+
+ struct pinctrl_desc pinctrl_desc;
+ struct gpio_chip gpio_chip;
+
+ void *irqsunmasked;
+ u8 irqgrps[];
+};
+
+//Register indexing
+#define REG_GPIO(x) (4 * (x))
+
+//Register map
+#define REG_GPIOx_MODE GENMASK(31, 30)
+#define REG_GPIOx_I BIT(28)
+#define REG_GPIOx_CLR BIT(26)
+#define REG_GPIOx_SET BIT(25)
+#define REG_GPIOx_O BIT(24)
+#define REG_GPIOx_INT_MASK BIT(22)
+#define REG_GPIOx_INT_STAT BIT(21)
+#define REG_GPIOx_INT_CLR BIT(20)
+#define REG_GPIOx_INT_MODE_SET GENMASK(29, 16)
+#define REG_GPIOx_FUNC_SEL GENMASK(12, 8)
+#define REG_GPIOx_OE BIT(6)
+#define REG_GPIOx_PD BIT(5)
+#define REG_GPIOx_PU BIT(4)
+#define REG_GPIOx_DRV GENMASK(3, 2)
+#define REG_GPIOx_SMT BIT(1)
+#define REG_GPIOx_IE BIT(0)
+
+//Interrupt trigger modes
+#define BFLB_IRQ_MODE_SYNC_EDGE_FALLING 0
+#define BFLB_IRQ_MODE_SYNC_EDGE_RISING 1
+#define BFLB_IRQ_MODE_SYNC_LEVEL_LOW 2
+#define BFLB_IRQ_MODE_SYNC_LEVEL_HIGH 3
+#define BFLB_IRQ_MODE_SYNC_EDGE_BOTH 4
+
+#define BFLB_IRQ_MODE_ASYNC_EDGE_FALLING 8
+#define BFLB_IRQ_MODE_ASYNC_EDGE_RISING 9
+#define BFLB_IRQ_MODE_ASYNC_LEVEL_LOW 10
+#define BFLB_IRQ_MODE_ASYNC_LEVEL_HIGH 11
+
+static const char * const pinmux_functions[] = {
+ //AH: As taken from smaeul's pinctrl-bflb.c for U-Boot
+ [0] = "sdh",
+ [1] = "spi0",
+ [2] = "flash",
+ [3] = "i2s",
+ [4] = "pdm",
+ [5] = "i2c0",
+ [6] = "i2c1",
+ [7] = "uart",
+ [8] = "emac",
+ [9] = "cam",
+ [10] = "analog",
+ [11] = "gpio",
+ [16] = "pwm0",
+ [17] = "pwm1",
+ [18] = "spi1", // mm_spi
+ [19] = "i2c2", // mm_i2c0
+ [20] = "i2c3", // mm_i2c1
+ [21] = "mm_uart",
+ [22] = "dbi_b",
+ [23] = "dbi_c",
+ [24] = "dpi",
+ [25] = "jtag_lp",
+ [26] = "jtag_m0",
+ [27] = "jtag_d0",
+ [31] = "clock",
+};
+
+struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .cache_type = REGCACHE_FLAT,
+ .max_register = 512 * sizeof(u32),
+ .num_reg_defaults_raw = 512,
+ .use_relaxed_mmio = true,
+ .use_raw_spinlock = true,
+};
+
+//AH: Set raw gpio config register bits based on mask
+static void bflb_gpio_set_reg(struct bflb_gpio_pinctrl *pctl, unsigned int pin,
+ u32 mask, u32 value)
+{
+ regmap_update_bits(pctl->map, REG_GPIO(pin), mask, value);
+}
+
+//AH: Get raw gpio config register bits
+static u32 bflb_gpio_get_reg(struct bflb_gpio_pinctrl *pctl, unsigned int pin)
+{
+ int ret;
+ u32 val;
+
+ ret = regmap_read(pctl->map, REG_GPIO(pin), &val);
+
+ if (ret)
+ return 0;
+
+ return val;
+}
+
+/* Pin controller functions */
+
+static const struct pinctrl_ops bflb_gpio_pinctrl_ops = {
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+ .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+/* Pin multiplexer functions */
+
+//AH: Configure gpio modes and features
+static int bflb_gpio_pinmux_set(struct pinctrl_dev *pctldev, unsigned int func,
+ unsigned int group)
+{
+ struct bflb_gpio_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ bflb_gpio_set_reg(pctl, group, REG_GPIOx_FUNC_SEL,
+ FIELD_PREP(REG_GPIOx_FUNC_SEL, func));
+
+ dev_dbg(pctl->dev, "Pin %u set to function %u", group, func);
+
+ return 0;
+}
+
+static const struct pinmux_ops bflb_gpio_pinmux_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = bflb_gpio_pinmux_set,
+ .strict = true,
+};
+
+/* GPIO chip functions */
+
+//AH: Get the current gpio direction
+static int bflb_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+ unsigned int reg = bflb_gpio_get_reg(pctl, offset);
+
+ if (FIELD_GET(REG_GPIOx_OE, reg) == 1 &&
+ FIELD_GET(REG_GPIOx_IE, reg) == 0) {
+ return GPIO_LINE_DIRECTION_OUT;
+ } else if (FIELD_GET(REG_GPIOx_IE, reg) == 1 &&
+ FIELD_GET(REG_GPIOx_OE, reg) == 0) {
+ return GPIO_LINE_DIRECTION_IN;
+ }
+
+ return -EIO;
+}
+
+//AH: Get the incoming input or outgoing output value for the specified GPIO
+static int bflb_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+ unsigned int reg = bflb_gpio_get_reg(pctl, offset);
+
+ if (FIELD_GET(REG_GPIOx_OE, reg) == 1 &&
+ FIELD_GET(REG_GPIOx_IE, reg) == 0) {
+ reg = readl_relaxed(pctl->base + REG_GPIO(offset));
+ return !!(reg & REG_GPIOx_O);
+ } else if (FIELD_GET(REG_GPIOx_IE, reg) == 1 &&
+ FIELD_GET(REG_GPIOx_OE, reg) == 0) {
+ reg = readl_relaxed(pctl->base + REG_GPIO(offset));
+ return !!(reg & REG_GPIOx_I);
+ }
+
+ return -EIO;
+}
+
+//AH: Set the specified GPIO's output state
+static void bflb_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_O, value ?
+ FIELD_PREP(REG_GPIOx_O, 1) : 0);
+
+ dev_dbg(pctl->dev, "Pin %u set to value %u", offset, value);
+}
+
+//AH: Set the specified gpio direction to input
+static int bflb_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_OE | REG_GPIOx_IE | REG_GPIOx_SMT,
+ FIELD_PREP(REG_GPIOx_OE, 0) | FIELD_PREP(REG_GPIOx_IE, 1) |
+ FIELD_PREP(REG_GPIOx_SMT, 1));
+
+ dev_dbg(pctl->dev, "Pin %u set to direction input", offset);
+
+ return 0;
+}
+
+//AH: Set the specified gpio direction to output
+static int bflb_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_PD | REG_GPIOx_PU | REG_GPIOx_OE |
+ REG_GPIOx_IE | REG_GPIOx_SMT | REG_GPIOx_MODE,
+ FIELD_PREP(REG_GPIOx_PD, 0) | FIELD_PREP(REG_GPIOx_PU, 0) |
+ FIELD_PREP(REG_GPIOx_OE, 1) | FIELD_PREP(REG_GPIOx_IE, 0) |
+ FIELD_PREP(REG_GPIOx_SMT, 1) | FIELD_PREP(REG_GPIOx_MODE, 0));
+
+ dev_dbg(pctl->dev, "Pin %u set to direction output", offset);
+
+ bflb_gpio_set(chip, offset, value); //Set the initially passed value
+
+ return 0;
+}
+
+//AH: Configure pin electrical characteristics
+static int bflb_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+ enum pin_config_param param = pinconf_to_config_param(config);
+ unsigned int arg = pinconf_to_config_argument(config);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_PD | REG_GPIOx_PU,
+ FIELD_PREP(REG_GPIOx_PD, 0) | FIELD_PREP(REG_GPIOx_PU, 0));
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+ if (arg) {
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_PD | REG_GPIOx_PU,
+ FIELD_PREP(REG_GPIOx_PD, 0) | FIELD_PREP(REG_GPIOx_PU, 0));
+ }
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (arg) {
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_PD | REG_GPIOx_PU,
+ FIELD_PREP(REG_GPIOx_PD, 1) | FIELD_PREP(REG_GPIOx_PU, 0));
+ }
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (arg) {
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_PD | REG_GPIOx_PU,
+ FIELD_PREP(REG_GPIOx_PD, 0) | FIELD_PREP(REG_GPIOx_PU, 1));
+ }
+ break;
+
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_PD | REG_GPIOx_PU |
+ REG_GPIOx_IE | REG_GPIOx_OE, FIELD_PREP(REG_GPIOx_PD, 0) |
+ FIELD_PREP(REG_GPIOx_PU, 0) | FIELD_PREP(REG_GPIOx_IE, 0) |
+ FIELD_PREP(REG_GPIOx_OE, 0));
+ break;
+
+ case PIN_CONFIG_INPUT_ENABLE:
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_IE,
+ FIELD_PREP(REG_GPIOx_IE, !!arg));
+ break;
+
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_SMT,
+ FIELD_PREP(REG_GPIOx_SMT, !!arg));
+ break;
+
+ case PIN_CONFIG_OUTPUT:
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_OE | REG_GPIOx_O,
+ FIELD_PREP(REG_GPIOx_OE, 1) | FIELD_PREP(REG_GPIOx_O, !!arg));
+ break;
+
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_OE,
+ FIELD_PREP(REG_GPIOx_OE, 1));
+ break;
+
+ default: return -ENOTSUPP;
+ }
+
+ dev_dbg(pctl->dev, "Pin %u config set to %lu (param %u, arg %u)", offset,
+ config, param, arg);
+
+ return 0;
+}
+
+/* IRQ chip functions */
+
+//AH: Clear the interrupt for the specified GPIO
+static void bflb_gpio_irq_ack(struct irq_data *data)
+{
+ struct bflb_gpio_pinctrl *pctl =
+ gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_CLR,
+ FIELD_PREP(REG_GPIOx_INT_CLR, 1));
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_CLR,
+ FIELD_PREP(REG_GPIOx_INT_CLR, 0));
+
+ dev_dbg(pctl->dev, "Pin %lu IRQ ACK", data->hwirq);
+}
+
+//AH: Find the correct value for the type of interrupts we want to receive
+//for a GPIO
+static unsigned int bflb_gpio_irq_type(unsigned int type)
+{
+ unsigned int selected;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+
+ case IRQ_TYPE_EDGE_RISING:
+ selected = BFLB_IRQ_MODE_SYNC_EDGE_RISING;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ selected = BFLB_IRQ_MODE_SYNC_EDGE_FALLING;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ selected = BFLB_IRQ_MODE_SYNC_EDGE_BOTH;
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ selected = BFLB_IRQ_MODE_SYNC_LEVEL_HIGH;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ selected = BFLB_IRQ_MODE_SYNC_LEVEL_LOW;
+ break;
+
+ //No "off" available on BL808, set to default IRQ_TYPE_EDGE_FALLING and
+ //then we'll need to mask
+ default:
+ selected = BFLB_IRQ_MODE_SYNC_EDGE_FALLING;
+ break;
+ }
+
+ return selected;
+}
+
+//AH: Disable the specified GPIO's interrupt
+static void bflb_gpio_irq_mask(struct irq_data *data)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(gc);
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_MASK,
+ FIELD_PREP(REG_GPIOx_INT_MASK, 1));
+
+ clear_bit(data->hwirq, pctl->irqsunmasked);
+ gpiochip_disable_irq(gc, data->hwirq);
+
+ dev_dbg(pctl->dev, "Pin %lu IRQ Mask", data->hwirq);
+}
+
+//AH: Enable the specified GPIO's interrupt
+static void bflb_gpio_irq_unmask(struct irq_data *data)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(gc);
+ unsigned int irqtype = bflb_gpio_irq_type(irqd_get_trigger_type(data));
+
+ gpiochip_enable_irq(gc, data->hwirq);
+ set_bit(data->hwirq, pctl->irqsunmasked);
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_MASK |
+ REG_GPIOx_INT_MODE_SET, FIELD_PREP(REG_GPIOx_INT_MASK, 0) |
+ FIELD_PREP(REG_GPIOx_INT_MODE_SET, irqtype));
+
+ dev_dbg(pctl->dev, "Pin %lu IRQ Unmask", data->hwirq);
+}
+
+//AH: Initialise the specified GPIO's interrupt
+static unsigned int bflb_gpio_irq_startup(struct irq_data *data)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_CLR,
+ FIELD_PREP(REG_GPIOx_INT_CLR, 1));
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_CLR,
+ FIELD_PREP(REG_GPIOx_INT_CLR, 0));
+
+ bflb_gpio_irq_unmask(data);
+
+ dev_dbg(pctl->dev, "Pin %lu IRQ Started", data->hwirq);
+
+ return 0;
+}
+
+//AH: Set the specified GPIO's interrupt mode
+static int bflb_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct bflb_gpio_pinctrl *pctl =
+ gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+ unsigned int irqtype = bflb_gpio_irq_type(type);
+
+ if (irqtype == 0)
+ return -EINVAL;
+
+ bflb_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_INT_MODE_SET,
+ FIELD_PREP(REG_GPIOx_INT_MODE_SET, irqtype));
+
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(data, handle_level_irq);
+ else
+ irq_set_handler_locked(data, handle_edge_irq);
+
+ dev_dbg(pctl->dev, "Pin %lu IRQ type set to %u", data->hwirq, irqtype);
+
+ return 0;
+}
+
+//AH: Handle GPIO interrupts on this controller
+static void bflb_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u8 *grpp = irq_desc_get_handler_data(desc);
+ struct bflb_gpio_pinctrl *pctl;
+ unsigned int pinh;
+ unsigned long reg;
+ struct gpio_chip *gc;
+
+ pctl = container_of(grpp - *grpp, typeof(*pctl), irqgrps[0]);
+ gc = &pctl->gpio_chip;
+
+ chained_irq_enter(chip, desc);
+
+ //We must go through each individual GPIO register to read its interrupt
+ //status. There is no gpio_cfg128+ helper register for interrupts
+ //(looking at BL808 RM)
+ for (pinh = 0; pinh < gc->ngpio; pinh += 1) {
+ if (test_bit(pinh, pctl->irqsunmasked)) {
+ dev_dbg(pctl->dev, "Reading IRQ status of pin %u", pinh);
+
+ reg = readl_relaxed(pctl->base + REG_GPIO(pinh));
+
+ if (reg & REG_GPIOx_INT_STAT) {
+ generic_handle_domain_irq(gc->irq.domain, pinh);
+ dev_dbg(pctl->dev, "Pin %u IRQ Fire", pinh);
+ }
+ } else {
+ dev_dbg(pctl->dev, "Ignoring IRQ status of masked pin %u", pinh);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static const struct irq_chip bflb_gpio_irqchip = {
+ .name = "bflb-gpio",
+ .irq_startup = bflb_gpio_irq_startup,
+ .irq_ack = bflb_gpio_irq_ack,
+ .irq_mask = bflb_gpio_irq_mask,
+ .irq_unmask = bflb_gpio_irq_unmask,
+ .irq_set_type = bflb_gpio_irq_set_type,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static int bflb_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ int ret;
+ struct bflb_gpio_pinctrl *pctl = gpiochip_get_data(chip);
+
+ ret = pinctrl_gpio_request(chip->base + offset);
+
+ if (ret)
+ return ret;
+
+ bflb_gpio_set_reg(pctl, offset, REG_GPIOx_FUNC_SEL,
+ FIELD_PREP(REG_GPIOx_FUNC_SEL, 11/*SWGPIO*/));
+
+ dev_dbg(pctl->dev, "Pin %u set to function GPIO as part of request",
+ offset);
+
+ return 0;
+}
+
+/* Probe & register */
+
+static int bflb_gpio_register(struct bflb_gpio_pinctrl *pctl)
+{
+ struct gpio_irq_chip *girq = &pctl->gpio_chip.irq;
+ void **irq_data = NULL;
+ int ret;
+
+ pctl->gpio_chip.label = dev_name(pctl->dev);
+ pctl->gpio_chip.request = bflb_gpio_request;
+ pctl->gpio_chip.free = gpiochip_generic_free;
+ pctl->gpio_chip.get_direction = bflb_gpio_get_direction;
+ pctl->gpio_chip.direction_input = bflb_gpio_direction_input;
+ pctl->gpio_chip.direction_output = bflb_gpio_direction_output;
+ pctl->gpio_chip.get = bflb_gpio_get;
+ pctl->gpio_chip.set = bflb_gpio_set;
+ pctl->gpio_chip.set_config = bflb_gpio_set_config;
+ pctl->gpio_chip.base = -1;
+ pctl->gpio_chip.ngpio = pctl->pinctrl_desc.npins;
+ pctl->gpio_chip.parent = pctl->dev;
+
+ if (girq->num_parents) {
+ int i;
+
+ gpio_irq_chip_set_chip(girq, &bflb_gpio_irqchip);
+
+ girq->parent_handler = bflb_gpio_irq_handler;
+
+ girq->parents = kmalloc_array(girq->num_parents, sizeof(*girq->parents),
+ GFP_KERNEL);
+ irq_data = kmalloc_array(girq->num_parents, sizeof(*irq_data),
+ GFP_KERNEL);
+
+ if (!girq->parents || !irq_data) {
+ ret = -ENOMEM;
+ goto out_free_irq_data;
+ }
+
+ for (i = 0; i < girq->num_parents; i++) {
+ ret = platform_get_irq(to_platform_device(pctl->dev), i);
+
+ if (ret < 0)
+ goto out_free_irq_data;
+
+ girq->parents[i] = ret;
+ pctl->irqgrps[i] = i;
+ irq_data[i] = &pctl->irqgrps[i];
+ }
+
+ girq->parent_handler_data_array = irq_data;
+ girq->per_parent_data = true;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_level_irq;
+ }
+
+ ret = devm_gpiochip_add_data(pctl->dev, &pctl->gpio_chip, pctl);
+
+out_free_irq_data:
+ kfree(girq->parents);
+ kfree(irq_data);
+
+ return ret;
+}
+
+static int bflb_gpio_pinctrl_probe(struct platform_device *pdev)
+{
+ struct bflb_gpio_pinctrl *pctl;
+ struct pinctrl_pin_desc *pins;
+
+ unsigned int npins;
+ const char **pin_names;
+ unsigned int *pin_nums;
+ unsigned int i, nirqs = 0;
+ int res;
+
+ if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) {
+ res = platform_irq_count(pdev);
+ if (res > 0)
+ nirqs = res;
+ }
+
+ pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
+
+ if (!pctl)
+ return -ENOMEM;
+
+ pctl->dev = &pdev->dev;
+ pctl->gpio_chip.irq.num_parents = nirqs;
+
+ dev_set_drvdata(&pdev->dev, pctl);
+
+ if (of_property_read_u32(pdev->dev.of_node, "bflb,npins", &npins))
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "bflb,npins property not found\n");
+
+ pctl->irqsunmasked = devm_bitmap_zalloc(&pdev->dev, npins, GFP_KERNEL);
+
+ pins = devm_kmalloc_array(&pdev->dev, npins, sizeof(pins[0]), GFP_KERNEL);
+ pin_names = devm_kmalloc_array(&pdev->dev, npins, sizeof(pin_names[0]),
+ GFP_KERNEL);
+ pin_nums = devm_kmalloc_array(&pdev->dev, npins, sizeof(pin_nums[0]),
+ GFP_KERNEL);
+
+ if (!pins || !pin_names || !pin_nums)
+ return -ENOMEM;
+
+ pctl->base = devm_platform_ioremap_resource(pdev, 0);
+
+ if (IS_ERR(pctl->base))
+ return PTR_ERR(pctl->base);
+
+ pctl->map = devm_regmap_init_mmio(&pdev->dev, pctl->base, &regmap_config);
+
+ if (IS_ERR(pctl->map))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pctl->map),
+ "Failed to create regmap\n");
+
+ for (i = 0; i < npins; i++) {
+ pins[i].number = i;
+ pins[i].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "GPIO%u", i);
+ pins[i].drv_data = pctl;
+ pin_names[i] = pins[i].name;
+ pin_nums[i] = i;
+ }
+
+ pctl->pinctrl_desc.name = dev_name(pctl->dev);
+ pctl->pinctrl_desc.pins = pins;
+ pctl->pinctrl_desc.npins = npins;
+ pctl->pinctrl_desc.pctlops = &bflb_gpio_pinctrl_ops;
+ pctl->pinctrl_desc.pmxops = &bflb_gpio_pinmux_ops;
+
+ pctl->pctldev = devm_pinctrl_register(&pdev->dev, &pctl->pinctrl_desc,
+ pctl);
+
+ if (IS_ERR(pctl->pctldev))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pctl->pctldev),
+ "Failed to register pinctrl device.\n");
+
+ for (i = 0; i < npins; i++) {
+ res = pinctrl_generic_add_group(pctl->pctldev,
+ pins[i].name, pin_nums + i, 1, pctl);
+
+ dev_dbg(&pdev->dev, "Registered pin %s with numeric %u",
+ pins[i].name, i);
+
+ if (res < 0)
+ return dev_err_probe(pctl->dev, res, "Failed to register group");
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pinmux_functions); ++i) {
+ if (pinmux_functions[i]) {
+ res = pinmux_generic_add_function(pctl->pctldev,
+ pinmux_functions[i], pin_names, npins, pctl);
+
+ dev_dbg(&pdev->dev, "Registered function %s with numeric %u",
+ pinmux_functions[i], i);
+
+ if (res < 0)
+ return dev_err_probe(pctl->dev, res,
+ "Failed to register function.");
+ }
+ }
+
+ dev_info(&pdev->dev, "Bouffalo Lab pinctrl+GPIO(+interrupt) controller - "
+ "Registered %lu function(s) for %u pin(s)",
+
+ ARRAY_SIZE(pinmux_functions), npins);
+
+ return bflb_gpio_register(pctl);
+}
+
+static const struct of_device_id bflb_gpio_pinctrl_of_match[] = {
+ { .compatible = "bflb,pinctrl", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, bflb_gpio_pinctrl_of_match);
+
+static struct platform_driver bflb_gpio_pinctrl_driver = {
+ .driver = {
+ .name = "bflb-gpio-pinctrl",
+ .of_match_table = bflb_gpio_pinctrl_of_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = bflb_gpio_pinctrl_probe,
+};
+
+module_platform_driver(bflb_gpio_pinctrl_driver);
+
+MODULE_DESCRIPTION("Bouffalo BL808 pinctrl/GPIO driver");
+MODULE_AUTHOR("Alexander Horner <contact@alexhorner.cc>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/mailbox/bflb-ipc.h b/include/dt-bindings/mailbox/bflb-ipc.h
index 0a3c6745a673..9763460edbcc 100644
--- a/include/dt-bindings/mailbox/bflb-ipc.h
+++ b/include/dt-bindings/mailbox/bflb-ipc.h
@@ -13,5 +13,8 @@
/* Peripheral device ID */
#define BFLB_IPC_DEVICE_SDHCI 0
#define BFLB_IPC_DEVICE_UART2 1
+#define BFLB_IPC_DEVICE_USB 2
+#define BFLB_IPC_DEVICE_EMAC 3
+#define BFLB_IPC_DEVICE_GPIO 4
#endif
--
2.25.1