0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/bits.h>
0011 #include <linux/gpio/driver.h>
0012 #include <linux/init.h>
0013 #include <linux/kernel.h>
0014 #include <linux/mfd/syscon.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/regmap.h>
0019
0020 #define PIOBU_NUM 8
0021 #define PIOBU_REG_SIZE 4
0022
0023
0024
0025
0026
0027
0028 #define PIOBU_BMPR 0x7C
0029 #define PIOBU_NMPR 0x80
0030 #define PIOBU_WKPR 0x90
0031
0032 #define PIOBU_BASE 0x18
0033
0034 #define PIOBU_DET_OFFSET 16
0035
0036
0037 #define PIOBU_DIRECTION BIT(8)
0038 #define PIOBU_OUT BIT(8)
0039 #define PIOBU_IN 0
0040
0041 #define PIOBU_SOD BIT(9)
0042 #define PIOBU_PDS BIT(10)
0043
0044 #define PIOBU_HIGH BIT(9)
0045 #define PIOBU_LOW 0
0046
0047 struct sama5d2_piobu {
0048 struct gpio_chip chip;
0049 struct regmap *regmap;
0050 };
0051
0052
0053
0054
0055
0056
0057
0058 static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
0059 {
0060 int ret;
0061 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
0062 chip);
0063 unsigned int mask = BIT(PIOBU_DET_OFFSET + pin);
0064
0065 ret = regmap_update_bits(piobu->regmap, PIOBU_BMPR, mask, 0);
0066 if (ret)
0067 return ret;
0068
0069 ret = regmap_update_bits(piobu->regmap, PIOBU_NMPR, mask, 0);
0070 if (ret)
0071 return ret;
0072
0073 return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0);
0074 }
0075
0076
0077
0078
0079 static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
0080 unsigned int mask, unsigned int value)
0081 {
0082 int reg;
0083 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
0084 chip);
0085
0086 reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
0087
0088 return regmap_update_bits(piobu->regmap, reg, mask, value);
0089 }
0090
0091
0092
0093
0094
0095 static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
0096 unsigned int mask)
0097 {
0098 struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
0099 chip);
0100 unsigned int val, reg;
0101 int ret;
0102
0103 reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
0104 ret = regmap_read(piobu->regmap, reg, &val);
0105 if (ret < 0)
0106 return ret;
0107
0108 return val & mask;
0109 }
0110
0111
0112
0113
0114 static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
0115 unsigned int pin)
0116 {
0117 int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION);
0118
0119 if (ret < 0)
0120 return ret;
0121
0122 return (ret == PIOBU_IN) ? GPIO_LINE_DIRECTION_IN :
0123 GPIO_LINE_DIRECTION_OUT;
0124 }
0125
0126
0127
0128
0129 static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
0130 unsigned int pin)
0131 {
0132 return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN);
0133 }
0134
0135
0136
0137
0138 static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
0139 unsigned int pin, int value)
0140 {
0141 unsigned int val = PIOBU_OUT;
0142
0143 if (value)
0144 val |= PIOBU_HIGH;
0145
0146 return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION | PIOBU_SOD,
0147 val);
0148 }
0149
0150
0151
0152
0153 static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
0154 {
0155
0156 int ret = sama5d2_piobu_get_direction(chip, pin);
0157
0158 if (ret == GPIO_LINE_DIRECTION_IN)
0159 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
0160 else if (ret == GPIO_LINE_DIRECTION_OUT)
0161 ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
0162
0163 if (ret < 0)
0164 return ret;
0165
0166 return !!ret;
0167 }
0168
0169
0170
0171
0172 static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
0173 int value)
0174 {
0175 if (!value)
0176 value = PIOBU_LOW;
0177 else
0178 value = PIOBU_HIGH;
0179
0180 sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);
0181 }
0182
0183 static int sama5d2_piobu_probe(struct platform_device *pdev)
0184 {
0185 struct sama5d2_piobu *piobu;
0186 int ret, i;
0187
0188 piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL);
0189 if (!piobu)
0190 return -ENOMEM;
0191
0192 platform_set_drvdata(pdev, piobu);
0193 piobu->chip.label = pdev->name;
0194 piobu->chip.parent = &pdev->dev;
0195 piobu->chip.owner = THIS_MODULE,
0196 piobu->chip.get_direction = sama5d2_piobu_get_direction,
0197 piobu->chip.direction_input = sama5d2_piobu_direction_input,
0198 piobu->chip.direction_output = sama5d2_piobu_direction_output,
0199 piobu->chip.get = sama5d2_piobu_get,
0200 piobu->chip.set = sama5d2_piobu_set,
0201 piobu->chip.base = -1,
0202 piobu->chip.ngpio = PIOBU_NUM,
0203 piobu->chip.can_sleep = 0,
0204
0205 piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node);
0206 if (IS_ERR(piobu->regmap)) {
0207 dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n",
0208 PTR_ERR(piobu->regmap));
0209 return PTR_ERR(piobu->regmap);
0210 }
0211
0212 ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu);
0213 if (ret) {
0214 dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret);
0215 return ret;
0216 }
0217
0218 for (i = 0; i < PIOBU_NUM; ++i) {
0219 ret = sama5d2_piobu_setup_pin(&piobu->chip, i);
0220 if (ret) {
0221 dev_err(&pdev->dev, "Failed to setup pin: %d %d\n",
0222 i, ret);
0223 return ret;
0224 }
0225 }
0226
0227 return 0;
0228 }
0229
0230 static const struct of_device_id sama5d2_piobu_ids[] = {
0231 { .compatible = "atmel,sama5d2-secumod" },
0232 {},
0233 };
0234 MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids);
0235
0236 static struct platform_driver sama5d2_piobu_driver = {
0237 .driver = {
0238 .name = "sama5d2-piobu",
0239 .of_match_table = of_match_ptr(sama5d2_piobu_ids)
0240 },
0241 .probe = sama5d2_piobu_probe,
0242 };
0243
0244 module_platform_driver(sama5d2_piobu_driver);
0245
0246 MODULE_LICENSE("GPL v2");
0247 MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
0248 MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");