Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | // SPDX-License-Identifier: GPL-2.0+ /* * Exynos pinctrl driver common code. * Copyright (C) 2016 Samsung Electronics * Thomas Abraham <thomas.ab@samsung.com> */ #include <log.h> #include <dm.h> #include <errno.h> #include <asm/io.h> #include "pinctrl-exynos.h" /* CON, DAT, PUD, DRV */ const struct samsung_pin_bank_type bank_type_alive = { .fld_width = { 4, 1, 2, 2, }, .reg_offset = { 0x00, 0x04, 0x08, 0x0c, }, }; static const char * const exynos_pinctrl_props[PINCFG_TYPE_NUM] = { [PINCFG_TYPE_FUNC] = "samsung,pin-function", [PINCFG_TYPE_DAT] = "samsung,pin-val", [PINCFG_TYPE_PUD] = "samsung,pin-pud", [PINCFG_TYPE_DRV] = "samsung,pin-drv", }; /** * exynos_pinctrl_setup_peri: setup pinctrl for a peripheral. * conf: soc specific pin configuration data array * num_conf: number of configurations in the conf array. * base: base address of the pin controller. */ void exynos_pinctrl_setup_peri(struct exynos_pinctrl_config_data *conf, unsigned int num_conf, unsigned long base) { unsigned int idx, val; for (idx = 0; idx < num_conf; idx++) { val = readl(base + conf[idx].offset); val &= ~(conf[idx].mask); val |= conf[idx].value; writel(val, base + conf[idx].offset); } } static void parse_pin(const char *pin_name, u32 *pin, char *bank_name) { u32 idx = 0; /* * The format of the pin name is <bank_name name>-<pin_number>. * Example: gpa0-4 (gpa0 is the bank_name name and 4 is the pin number. */ while (pin_name[idx] != '-') { bank_name[idx] = pin_name[idx]; idx++; } bank_name[idx] = '\0'; *pin = pin_name[++idx] - '0'; } /* given a bank name, find out the pin bank structure */ static const struct samsung_pin_bank_data *get_bank(struct udevice *dev, const char *bank_name) { struct exynos_pinctrl_priv *priv = dev_get_priv(dev); const struct samsung_pin_ctrl *pin_ctrl_array = priv->pin_ctrl; const struct samsung_pin_bank_data *bank_data; u32 nr_banks, pin_ctrl_idx = 0, idx = 0; /* lookup the pin bank data using the pin bank name */ while (true) { const struct samsung_pin_ctrl *pin_ctrl = &pin_ctrl_array[pin_ctrl_idx]; nr_banks = pin_ctrl->nr_banks; if (!nr_banks) break; bank_data = pin_ctrl->pin_banks; for (idx = 0; idx < nr_banks; idx++) { debug("pinctrl[%d] bank_data[%d] name is: %s\n", pin_ctrl_idx, idx, bank_data[idx].name); if (!strcmp(bank_name, bank_data[idx].name)) return &bank_data[idx]; } pin_ctrl_idx++; } return NULL; } static void exynos_pinctrl_set_pincfg(unsigned long reg_base, u32 pin_num, u32 val, enum pincfg_type pincfg, const struct samsung_pin_bank_type *type) { u32 width = type->fld_width[pincfg]; u32 reg_offset = type->reg_offset[pincfg]; u32 mask = (1 << width) - 1; u32 shift = pin_num * width; u32 data; data = readl(reg_base + reg_offset); data &= ~(mask << shift); data |= val << shift; writel(data, reg_base + reg_offset); } /** * exynos_pinctrl_set_state: configure a pin state. * dev: the pinctrl device to be configured. * config: the state to be configured. */ int exynos_pinctrl_set_state(struct udevice *dev, struct udevice *config) { struct exynos_pinctrl_priv *priv = dev_get_priv(dev); int count; unsigned int idx, pinvals[PINCFG_TYPE_NUM]; /* * refer to the following document for the pinctrl bindings * linux/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt */ count = dev_read_string_count(config, "samsung,pins"); if (count <= 0) return -EINVAL; for (idx = 0; idx < PINCFG_TYPE_NUM; ++idx) { pinvals[idx] = dev_read_u32_default(config, exynos_pinctrl_props[idx], -1); } pinvals[PINCFG_TYPE_DAT] = -1; /* ignore GPIO data register */ for (idx = 0; idx < count; idx++) { const struct samsung_pin_bank_data *bank; unsigned int pin_num; char bank_name[10]; unsigned long reg; const char *name = NULL; int pincfg, err; err = dev_read_string_index(config, "samsung,pins", idx, &name); if (err || !name) continue; parse_pin(name, &pin_num, bank_name); bank = get_bank(dev, bank_name); reg = priv->base + bank->offset; for (pincfg = 0; pincfg < PINCFG_TYPE_NUM; ++pincfg) { unsigned int val = pinvals[pincfg]; if (val != -1) exynos_pinctrl_set_pincfg(reg, pin_num, val, pincfg, bank->type); } } return 0; } int exynos_pinctrl_probe(struct udevice *dev) { struct exynos_pinctrl_priv *priv; fdt_addr_t base; priv = dev_get_priv(dev); if (!priv) return -EINVAL; base = dev_read_addr(dev); if (base == FDT_ADDR_T_NONE) return -EINVAL; priv->base = base; priv->pin_ctrl = (struct samsung_pin_ctrl *)dev_get_driver_data(dev) + dev_seq(dev); return 0; } |