diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2018-11-28 08:57:23 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@armlinux.org.uk> | 2018-12-04 17:37:38 -0500 |
commit | bebdf98a786292501e4f7d596ffe61faaf3a3aca (patch) | |
tree | f65dbd50a6707f012a2f5e5652451273dc2ac421 | |
parent | d66a2fb8d7b0a103f5d6884ff9db895959bf0924 (diff) |
pcmcia: add MAX1600 library
Add a library for the MAX1600 PCMCIA power switch device. This is a
dual-channel device, controlled via four GPIO signals per channel.
Two signals control the Vcc output, and two control the Vpp output.
Acked-by: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r-- | drivers/pcmcia/Kconfig | 3 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 1 | ||||
-rw-r--r-- | drivers/pcmcia/max1600.c | 122 | ||||
-rw-r--r-- | drivers/pcmcia/max1600.h | 32 |
4 files changed, 158 insertions, 0 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index cbbe4a285b48..1a257a323923 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig | |||
@@ -63,6 +63,9 @@ config CARDBUS | |||
63 | 63 | ||
64 | If unsure, say Y. | 64 | If unsure, say Y. |
65 | 65 | ||
66 | config PCMCIA_MAX1600 | ||
67 | tristate | ||
68 | |||
66 | comment "PC-card bridges" | 69 | comment "PC-card bridges" |
67 | 70 | ||
68 | config YENTA | 71 | config YENTA |
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 28502bd159e0..01779c5c45f3 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile | |||
@@ -35,6 +35,7 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o | |||
35 | obj-$(CONFIG_AT91_CF) += at91_cf.o | 35 | obj-$(CONFIG_AT91_CF) += at91_cf.o |
36 | obj-$(CONFIG_ELECTRA_CF) += electra_cf.o | 36 | obj-$(CONFIG_ELECTRA_CF) += electra_cf.o |
37 | obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o | 37 | obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o |
38 | obj-$(CONFIG_PCMCIA_MAX1600) += max1600.o | ||
38 | 39 | ||
39 | sa1111_cs-y += sa1111_generic.o | 40 | sa1111_cs-y += sa1111_generic.o |
40 | sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o | 41 | sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o |
diff --git a/drivers/pcmcia/max1600.c b/drivers/pcmcia/max1600.c new file mode 100644 index 000000000000..379875a5e7cd --- /dev/null +++ b/drivers/pcmcia/max1600.c | |||
@@ -0,0 +1,122 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * MAX1600 PCMCIA power switch library | ||
4 | * | ||
5 | * Copyright (C) 2016 Russell King | ||
6 | */ | ||
7 | #include <linux/device.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/gpio/consumer.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include "max1600.h" | ||
12 | |||
13 | static const char *max1600_gpio_name[2][MAX1600_GPIO_MAX] = { | ||
14 | { "a0vcc", "a1vcc", "a0vpp", "a1vpp" }, | ||
15 | { "b0vcc", "b1vcc", "b0vpp", "b1vpp" }, | ||
16 | }; | ||
17 | |||
18 | int max1600_init(struct device *dev, struct max1600 **ptr, | ||
19 | unsigned int channel, unsigned int code) | ||
20 | { | ||
21 | struct max1600 *m; | ||
22 | int chan; | ||
23 | int i; | ||
24 | |||
25 | switch (channel) { | ||
26 | case MAX1600_CHAN_A: | ||
27 | chan = 0; | ||
28 | break; | ||
29 | case MAX1600_CHAN_B: | ||
30 | chan = 1; | ||
31 | break; | ||
32 | default: | ||
33 | return -EINVAL; | ||
34 | } | ||
35 | |||
36 | if (code != MAX1600_CODE_LOW && code != MAX1600_CODE_HIGH) | ||
37 | return -EINVAL; | ||
38 | |||
39 | m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL); | ||
40 | if (!m) | ||
41 | return -ENOMEM; | ||
42 | |||
43 | m->dev = dev; | ||
44 | m->code = code; | ||
45 | |||
46 | for (i = 0; i < MAX1600_GPIO_MAX; i++) { | ||
47 | const char *name; | ||
48 | |||
49 | name = max1600_gpio_name[chan][i]; | ||
50 | if (i != MAX1600_GPIO_0VPP) { | ||
51 | m->gpio[i] = devm_gpiod_get(dev, name, GPIOD_OUT_LOW); | ||
52 | } else { | ||
53 | m->gpio[i] = devm_gpiod_get_optional(dev, name, | ||
54 | GPIOD_OUT_LOW); | ||
55 | if (!m->gpio[i]) | ||
56 | break; | ||
57 | } | ||
58 | if (IS_ERR(m->gpio[i])) | ||
59 | return PTR_ERR(m->gpio[i]); | ||
60 | } | ||
61 | |||
62 | *ptr = m; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | EXPORT_SYMBOL_GPL(max1600_init); | ||
67 | |||
68 | int max1600_configure(struct max1600 *m, unsigned int vcc, unsigned int vpp) | ||
69 | { | ||
70 | DECLARE_BITMAP(values, MAX1600_GPIO_MAX) = { 0, }; | ||
71 | int n = MAX1600_GPIO_0VPP; | ||
72 | |||
73 | if (m->gpio[MAX1600_GPIO_0VPP]) { | ||
74 | if (vpp == 0) { | ||
75 | __assign_bit(MAX1600_GPIO_0VPP, values, 0); | ||
76 | __assign_bit(MAX1600_GPIO_1VPP, values, 0); | ||
77 | } else if (vpp == 120) { | ||
78 | __assign_bit(MAX1600_GPIO_0VPP, values, 0); | ||
79 | __assign_bit(MAX1600_GPIO_1VPP, values, 1); | ||
80 | } else if (vpp == vcc) { | ||
81 | __assign_bit(MAX1600_GPIO_0VPP, values, 1); | ||
82 | __assign_bit(MAX1600_GPIO_1VPP, values, 0); | ||
83 | } else { | ||
84 | dev_err(m->dev, "unrecognised Vpp %u.%uV\n", | ||
85 | vpp / 10, vpp % 10); | ||
86 | return -EINVAL; | ||
87 | } | ||
88 | n = MAX1600_GPIO_MAX; | ||
89 | } else if (vpp != vcc && vpp != 0) { | ||
90 | dev_err(m->dev, "no VPP control\n"); | ||
91 | return -EINVAL; | ||
92 | } | ||
93 | |||
94 | if (vcc == 0) { | ||
95 | __assign_bit(MAX1600_GPIO_0VCC, values, 0); | ||
96 | __assign_bit(MAX1600_GPIO_1VCC, values, 0); | ||
97 | } else if (vcc == 33) { /* VY */ | ||
98 | __assign_bit(MAX1600_GPIO_0VCC, values, 1); | ||
99 | __assign_bit(MAX1600_GPIO_1VCC, values, 0); | ||
100 | } else if (vcc == 50) { /* VX */ | ||
101 | __assign_bit(MAX1600_GPIO_0VCC, values, 0); | ||
102 | __assign_bit(MAX1600_GPIO_1VCC, values, 1); | ||
103 | } else { | ||
104 | dev_err(m->dev, "unrecognised Vcc %u.%uV\n", | ||
105 | vcc / 10, vcc % 10); | ||
106 | return -EINVAL; | ||
107 | } | ||
108 | |||
109 | if (m->code == MAX1600_CODE_HIGH) { | ||
110 | /* | ||
111 | * Cirrus mode appears to be the same as Intel mode, | ||
112 | * except the VCC pins are inverted. | ||
113 | */ | ||
114 | __change_bit(MAX1600_GPIO_0VCC, values); | ||
115 | __change_bit(MAX1600_GPIO_1VCC, values); | ||
116 | } | ||
117 | |||
118 | return gpiod_set_array_value_cansleep(n, m->gpio, NULL, values); | ||
119 | } | ||
120 | EXPORT_SYMBOL_GPL(max1600_configure); | ||
121 | |||
122 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pcmcia/max1600.h b/drivers/pcmcia/max1600.h new file mode 100644 index 000000000000..00bf1a09464f --- /dev/null +++ b/drivers/pcmcia/max1600.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef MAX1600_H | ||
3 | #define MAX1600_H | ||
4 | |||
5 | struct gpio_desc; | ||
6 | |||
7 | enum { | ||
8 | MAX1600_GPIO_0VCC = 0, | ||
9 | MAX1600_GPIO_1VCC, | ||
10 | MAX1600_GPIO_0VPP, | ||
11 | MAX1600_GPIO_1VPP, | ||
12 | MAX1600_GPIO_MAX, | ||
13 | |||
14 | MAX1600_CHAN_A, | ||
15 | MAX1600_CHAN_B, | ||
16 | |||
17 | MAX1600_CODE_LOW, | ||
18 | MAX1600_CODE_HIGH, | ||
19 | }; | ||
20 | |||
21 | struct max1600 { | ||
22 | struct gpio_desc *gpio[MAX1600_GPIO_MAX]; | ||
23 | struct device *dev; | ||
24 | unsigned int code; | ||
25 | }; | ||
26 | |||
27 | int max1600_init(struct device *dev, struct max1600 **ptr, | ||
28 | unsigned int channel, unsigned int code); | ||
29 | |||
30 | int max1600_configure(struct max1600 *, unsigned int vcc, unsigned int vpp); | ||
31 | |||
32 | #endif | ||