1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * MC33880 high-side/low-side switch GPIO driver |
4 | * Copyright (c) 2009 Intel Corporation |
5 | */ |
6 | |
7 | /* Supports: |
8 | * Freescale MC33880 high-side/low-side switch |
9 | */ |
10 | |
11 | #include <linux/init.h> |
12 | #include <linux/mutex.h> |
13 | #include <linux/spi/spi.h> |
14 | #include <linux/spi/mc33880.h> |
15 | #include <linux/gpio/driver.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/module.h> |
18 | |
19 | #define DRIVER_NAME "mc33880" |
20 | |
21 | /* |
22 | * Pin configurations, see MAX7301 datasheet page 6 |
23 | */ |
24 | #define PIN_CONFIG_MASK 0x03 |
25 | #define PIN_CONFIG_IN_PULLUP 0x03 |
26 | #define PIN_CONFIG_IN_WO_PULLUP 0x02 |
27 | #define PIN_CONFIG_OUT 0x01 |
28 | |
29 | #define PIN_NUMBER 8 |
30 | |
31 | |
32 | /* |
33 | * Some registers must be read back to modify. |
34 | * To save time we cache them here in memory |
35 | */ |
36 | struct mc33880 { |
37 | struct mutex lock; /* protect from simultaneous accesses */ |
38 | u8 port_config; |
39 | struct gpio_chip chip; |
40 | struct spi_device *spi; |
41 | }; |
42 | |
43 | static int mc33880_write_config(struct mc33880 *mc) |
44 | { |
45 | return spi_write(spi: mc->spi, buf: &mc->port_config, len: sizeof(mc->port_config)); |
46 | } |
47 | |
48 | |
49 | static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) |
50 | { |
51 | if (value) |
52 | mc->port_config |= 1 << offset; |
53 | else |
54 | mc->port_config &= ~(1 << offset); |
55 | |
56 | return mc33880_write_config(mc); |
57 | } |
58 | |
59 | |
60 | static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) |
61 | { |
62 | struct mc33880 *mc = gpiochip_get_data(gc: chip); |
63 | |
64 | mutex_lock(&mc->lock); |
65 | |
66 | __mc33880_set(mc, offset, value); |
67 | |
68 | mutex_unlock(lock: &mc->lock); |
69 | } |
70 | |
71 | static int mc33880_probe(struct spi_device *spi) |
72 | { |
73 | struct mc33880 *mc; |
74 | struct mc33880_platform_data *pdata; |
75 | int ret; |
76 | |
77 | pdata = dev_get_platdata(dev: &spi->dev); |
78 | if (!pdata || !pdata->base) { |
79 | dev_dbg(&spi->dev, "incorrect or missing platform data\n" ); |
80 | return -EINVAL; |
81 | } |
82 | |
83 | /* |
84 | * bits_per_word cannot be configured in platform data |
85 | */ |
86 | spi->bits_per_word = 8; |
87 | |
88 | ret = spi_setup(spi); |
89 | if (ret < 0) |
90 | return ret; |
91 | |
92 | mc = devm_kzalloc(dev: &spi->dev, size: sizeof(struct mc33880), GFP_KERNEL); |
93 | if (!mc) |
94 | return -ENOMEM; |
95 | |
96 | mutex_init(&mc->lock); |
97 | |
98 | spi_set_drvdata(spi, data: mc); |
99 | |
100 | mc->spi = spi; |
101 | |
102 | mc->chip.label = DRIVER_NAME, |
103 | mc->chip.set = mc33880_set; |
104 | mc->chip.base = pdata->base; |
105 | mc->chip.ngpio = PIN_NUMBER; |
106 | mc->chip.can_sleep = true; |
107 | mc->chip.parent = &spi->dev; |
108 | mc->chip.owner = THIS_MODULE; |
109 | |
110 | mc->port_config = 0x00; |
111 | /* write twice, because during initialisation the first setting |
112 | * is just for testing SPI communication, and the second is the |
113 | * "real" configuration |
114 | */ |
115 | ret = mc33880_write_config(mc); |
116 | mc->port_config = 0x00; |
117 | if (!ret) |
118 | ret = mc33880_write_config(mc); |
119 | |
120 | if (ret) { |
121 | dev_err(&spi->dev, "Failed writing to " DRIVER_NAME ": %d\n" , |
122 | ret); |
123 | goto exit_destroy; |
124 | } |
125 | |
126 | ret = gpiochip_add_data(&mc->chip, mc); |
127 | if (ret) |
128 | goto exit_destroy; |
129 | |
130 | return ret; |
131 | |
132 | exit_destroy: |
133 | mutex_destroy(lock: &mc->lock); |
134 | return ret; |
135 | } |
136 | |
137 | static void mc33880_remove(struct spi_device *spi) |
138 | { |
139 | struct mc33880 *mc; |
140 | |
141 | mc = spi_get_drvdata(spi); |
142 | |
143 | gpiochip_remove(gc: &mc->chip); |
144 | mutex_destroy(lock: &mc->lock); |
145 | } |
146 | |
147 | static struct spi_driver mc33880_driver = { |
148 | .driver = { |
149 | .name = DRIVER_NAME, |
150 | }, |
151 | .probe = mc33880_probe, |
152 | .remove = mc33880_remove, |
153 | }; |
154 | |
155 | static int __init mc33880_init(void) |
156 | { |
157 | return spi_register_driver(&mc33880_driver); |
158 | } |
159 | /* register after spi postcore initcall and before |
160 | * subsys initcalls that may rely on these GPIOs |
161 | */ |
162 | subsys_initcall(mc33880_init); |
163 | |
164 | static void __exit mc33880_exit(void) |
165 | { |
166 | spi_unregister_driver(sdrv: &mc33880_driver); |
167 | } |
168 | module_exit(mc33880_exit); |
169 | |
170 | MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>" ); |
171 | MODULE_LICENSE("GPL v2" ); |
172 | |
173 | |