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
13static const char *max1600_gpio_name[2][MAX1600_GPIO_MAX] = {
14	{ "a0vcc", "a1vcc", "a0vpp", "a1vpp" },
15	{ "b0vcc", "b1vcc", "b0vpp", "b1vpp" },
16};
17
18int 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}
66EXPORT_SYMBOL_GPL(max1600_init);
67
68int 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}
120EXPORT_SYMBOL_GPL(max1600_configure);
121
122MODULE_LICENSE("GPL v2");
123