forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
2.8 KiB
122 lines
2.8 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* MAX1600 PCMCIA power switch library |
|
* |
|
* Copyright (C) 2016 Russell King |
|
*/ |
|
#include <linux/device.h> |
|
#include <linux/module.h> |
|
#include <linux/gpio/consumer.h> |
|
#include <linux/slab.h> |
|
#include "max1600.h" |
|
|
|
static const char *max1600_gpio_name[2][MAX1600_GPIO_MAX] = { |
|
{ "a0vcc", "a1vcc", "a0vpp", "a1vpp" }, |
|
{ "b0vcc", "b1vcc", "b0vpp", "b1vpp" }, |
|
}; |
|
|
|
int max1600_init(struct device *dev, struct max1600 **ptr, |
|
unsigned int channel, unsigned int code) |
|
{ |
|
struct max1600 *m; |
|
int chan; |
|
int i; |
|
|
|
switch (channel) { |
|
case MAX1600_CHAN_A: |
|
chan = 0; |
|
break; |
|
case MAX1600_CHAN_B: |
|
chan = 1; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
if (code != MAX1600_CODE_LOW && code != MAX1600_CODE_HIGH) |
|
return -EINVAL; |
|
|
|
m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL); |
|
if (!m) |
|
return -ENOMEM; |
|
|
|
m->dev = dev; |
|
m->code = code; |
|
|
|
for (i = 0; i < MAX1600_GPIO_MAX; i++) { |
|
const char *name; |
|
|
|
name = max1600_gpio_name[chan][i]; |
|
if (i != MAX1600_GPIO_0VPP) { |
|
m->gpio[i] = devm_gpiod_get(dev, name, GPIOD_OUT_LOW); |
|
} else { |
|
m->gpio[i] = devm_gpiod_get_optional(dev, name, |
|
GPIOD_OUT_LOW); |
|
if (!m->gpio[i]) |
|
break; |
|
} |
|
if (IS_ERR(m->gpio[i])) |
|
return PTR_ERR(m->gpio[i]); |
|
} |
|
|
|
*ptr = m; |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(max1600_init); |
|
|
|
int max1600_configure(struct max1600 *m, unsigned int vcc, unsigned int vpp) |
|
{ |
|
DECLARE_BITMAP(values, MAX1600_GPIO_MAX) = { 0, }; |
|
int n = MAX1600_GPIO_0VPP; |
|
|
|
if (m->gpio[MAX1600_GPIO_0VPP]) { |
|
if (vpp == 0) { |
|
__assign_bit(MAX1600_GPIO_0VPP, values, 0); |
|
__assign_bit(MAX1600_GPIO_1VPP, values, 0); |
|
} else if (vpp == 120) { |
|
__assign_bit(MAX1600_GPIO_0VPP, values, 0); |
|
__assign_bit(MAX1600_GPIO_1VPP, values, 1); |
|
} else if (vpp == vcc) { |
|
__assign_bit(MAX1600_GPIO_0VPP, values, 1); |
|
__assign_bit(MAX1600_GPIO_1VPP, values, 0); |
|
} else { |
|
dev_err(m->dev, "unrecognised Vpp %u.%uV\n", |
|
vpp / 10, vpp % 10); |
|
return -EINVAL; |
|
} |
|
n = MAX1600_GPIO_MAX; |
|
} else if (vpp != vcc && vpp != 0) { |
|
dev_err(m->dev, "no VPP control\n"); |
|
return -EINVAL; |
|
} |
|
|
|
if (vcc == 0) { |
|
__assign_bit(MAX1600_GPIO_0VCC, values, 0); |
|
__assign_bit(MAX1600_GPIO_1VCC, values, 0); |
|
} else if (vcc == 33) { /* VY */ |
|
__assign_bit(MAX1600_GPIO_0VCC, values, 1); |
|
__assign_bit(MAX1600_GPIO_1VCC, values, 0); |
|
} else if (vcc == 50) { /* VX */ |
|
__assign_bit(MAX1600_GPIO_0VCC, values, 0); |
|
__assign_bit(MAX1600_GPIO_1VCC, values, 1); |
|
} else { |
|
dev_err(m->dev, "unrecognised Vcc %u.%uV\n", |
|
vcc / 10, vcc % 10); |
|
return -EINVAL; |
|
} |
|
|
|
if (m->code == MAX1600_CODE_HIGH) { |
|
/* |
|
* Cirrus mode appears to be the same as Intel mode, |
|
* except the VCC pins are inverted. |
|
*/ |
|
__change_bit(MAX1600_GPIO_0VCC, values); |
|
__change_bit(MAX1600_GPIO_1VCC, values); |
|
} |
|
|
|
return gpiod_set_array_value_cansleep(n, m->gpio, NULL, values); |
|
} |
|
EXPORT_SYMBOL_GPL(max1600_configure); |
|
|
|
MODULE_LICENSE("GPL v2");
|
|
|