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, size: 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, con_id: name, flags: GPIOD_OUT_LOW); |
52 | } else { |
53 | m->gpio[i] = devm_gpiod_get_optional(dev, con_id: name, |
54 | flags: GPIOD_OUT_LOW); |
55 | if (!m->gpio[i]) |
56 | break; |
57 | } |
58 | if (IS_ERR(ptr: m->gpio[i])) |
59 | return PTR_ERR(ptr: 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(nr: MAX1600_GPIO_0VPP, addr: values, value: 0); |
76 | __assign_bit(nr: MAX1600_GPIO_1VPP, addr: values, value: 0); |
77 | } else if (vpp == 120) { |
78 | __assign_bit(nr: MAX1600_GPIO_0VPP, addr: values, value: 0); |
79 | __assign_bit(nr: MAX1600_GPIO_1VPP, addr: values, value: 1); |
80 | } else if (vpp == vcc) { |
81 | __assign_bit(nr: MAX1600_GPIO_0VPP, addr: values, value: 1); |
82 | __assign_bit(nr: MAX1600_GPIO_1VPP, addr: values, value: 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(nr: MAX1600_GPIO_0VCC, addr: values, value: 0); |
96 | __assign_bit(nr: MAX1600_GPIO_1VCC, addr: values, value: 0); |
97 | } else if (vcc == 33) { /* VY */ |
98 | __assign_bit(nr: MAX1600_GPIO_0VCC, addr: values, value: 1); |
99 | __assign_bit(nr: MAX1600_GPIO_1VCC, addr: values, value: 0); |
100 | } else if (vcc == 50) { /* VX */ |
101 | __assign_bit(nr: MAX1600_GPIO_0VCC, addr: values, value: 0); |
102 | __assign_bit(nr: MAX1600_GPIO_1VCC, addr: values, value: 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(array_size: n, desc_array: m->gpio, NULL, value_bitmap: values); |
119 | } |
120 | EXPORT_SYMBOL_GPL(max1600_configure); |
121 | |
122 | MODULE_LICENSE("GPL v2" ); |
123 | |