Merge tag 'pinctrl-v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[sfrench/cifs-2.6.git] / drivers / pinctrl / aspeed / pinctrl-aspeed-g4.c
index 73e2c9c0e5496e9bf0d873adc30c9325a978a909..384396cbb22d21a00bf6f71f6fe7881bbfabf475 100644 (file)
 
 #include "../core.h"
 #include "../pinctrl-utils.h"
+#include "pinmux-aspeed.h"
 #include "pinctrl-aspeed.h"
 
+/*
+ * The "Multi-function Pins Mapping and Control" table in the SoC datasheet
+ * references registers by the device/offset mnemonic. The register macros
+ * below are named the same way to ease transcription and verification (as
+ * opposed to naming them e.g. PINMUX_CTRL_[0-9]). Further, signal expressions
+ * reference registers beyond those dedicated to pinmux, such as the system
+ * reset control and MAC clock configuration registers.
+ */
+#define SCU2C           0x2C /* Misc. Control Register */
+#define SCU3C           0x3C /* System Reset Control/Status Register */
+#define SCU48           0x48 /* MAC Interface Clock Delay Setting */
+#define HW_STRAP1       0x70 /* AST2400 strapping is 33 bits, is split */
+#define HW_REVISION_ID  0x7C /* Silicon revision ID register */
+#define SCU80           0x80 /* Multi-function Pin Control #1 */
+#define SCU84           0x84 /* Multi-function Pin Control #2 */
+#define SCU88           0x88 /* Multi-function Pin Control #3 */
+#define SCU8C           0x8C /* Multi-function Pin Control #4 */
+#define SCU90           0x90 /* Multi-function Pin Control #5 */
+#define SCU94           0x94 /* Multi-function Pin Control #6 */
+#define SCUA0           0xA0 /* Multi-function Pin Control #7 */
+#define SCUA4           0xA4 /* Multi-function Pin Control #8 */
+#define SCUA8           0xA8 /* Multi-function Pin Control #9 */
+#define SCUAC           0xAC /* Multi-function Pin Control #10 */
+#define HW_STRAP2       0xD0 /* Strapping */
+
 /*
  * Uses undefined macros for symbol naming and references, eg GPIOA0, MAC1LINK,
  * TIMER3 etc.
@@ -2386,13 +2412,73 @@ static const struct aspeed_pin_config aspeed_g4_configs[] = {
        { PIN_CONFIG_INPUT_DEBOUNCE, { C14, B14 }, SCUA8, 27 },
 };
 
+static int aspeed_g4_sig_expr_set(const struct aspeed_pinmux_data *ctx,
+                                 const struct aspeed_sig_expr *expr,
+                                 bool enable)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < expr->ndescs; i++) {
+               const struct aspeed_sig_desc *desc = &expr->descs[i];
+               u32 pattern = enable ? desc->enable : desc->disable;
+               u32 val = (pattern << __ffs(desc->mask));
+
+               if (!ctx->maps[desc->ip])
+                       return -ENODEV;
+
+               /*
+                * Strap registers are configured in hardware or by early-boot
+                * firmware. Treat them as read-only despite that we can write
+                * them. This may mean that certain functions cannot be
+                * deconfigured and is the reason we re-evaluate after writing
+                * all descriptor bits.
+                *
+                * Port D and port E GPIO loopback modes are the only exception
+                * as those are commonly used with front-panel buttons to allow
+                * normal operation of the host when the BMC is powered off or
+                * fails to boot. Once the BMC has booted, the loopback mode
+                * must be disabled for the BMC to control host power-on and
+                * reset.
+                */
+               if (desc->ip == ASPEED_IP_SCU && desc->reg == HW_STRAP1 &&
+                   !(desc->mask & (BIT(21) | BIT(22))))
+                       continue;
+
+               if (desc->ip == ASPEED_IP_SCU && desc->reg == HW_STRAP2)
+                       continue;
+
+               ret = regmap_update_bits(ctx->maps[desc->ip], desc->reg,
+                                        desc->mask, val);
+
+               if (ret)
+                       return ret;
+       }
+
+       ret = aspeed_sig_expr_eval(ctx, expr, enable);
+       if (ret < 0)
+               return ret;
+
+       if (!ret)
+               return -EPERM;
+
+       return 0;
+}
+
+static const struct aspeed_pinmux_ops aspeed_g4_ops = {
+       .set = aspeed_g4_sig_expr_set,
+};
+
 static struct aspeed_pinctrl_data aspeed_g4_pinctrl_data = {
        .pins = aspeed_g4_pins,
        .npins = ARRAY_SIZE(aspeed_g4_pins),
-       .groups = aspeed_g4_groups,
-       .ngroups = ARRAY_SIZE(aspeed_g4_groups),
-       .functions = aspeed_g4_functions,
-       .nfunctions = ARRAY_SIZE(aspeed_g4_functions),
+       .pinmux = {
+               .ops = &aspeed_g4_ops,
+               .groups = aspeed_g4_groups,
+               .ngroups = ARRAY_SIZE(aspeed_g4_groups),
+               .functions = aspeed_g4_functions,
+               .nfunctions = ARRAY_SIZE(aspeed_g4_functions),
+       },
        .configs = aspeed_g4_configs,
        .nconfigs = ARRAY_SIZE(aspeed_g4_configs),
 };