Thomas Gleixner | d2912cb | 2019-06-04 10:11:33 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 2 | /* |
| 3 | * arizona-spi.c -- Arizona SPI bus interface |
| 4 | * |
| 5 | * Copyright 2012 Wolfson Microelectronics plc |
| 6 | * |
| 7 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 8 | */ |
| 9 | |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 10 | #include <linux/acpi.h> |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 11 | #include <linux/err.h> |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 12 | #include <linux/gpio/consumer.h> |
| 13 | #include <linux/gpio/machine.h> |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 14 | #include <linux/module.h> |
| 15 | #include <linux/pm_runtime.h> |
| 16 | #include <linux/regmap.h> |
| 17 | #include <linux/regulator/consumer.h> |
| 18 | #include <linux/slab.h> |
| 19 | #include <linux/spi/spi.h> |
Sachin Kamat | 3d6d1d1 | 2013-10-16 14:26:56 +0530 | [diff] [blame] | 20 | #include <linux/of.h> |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 21 | #include <uapi/linux/input-event-codes.h> |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 22 | |
| 23 | #include <linux/mfd/arizona/core.h> |
| 24 | |
| 25 | #include "arizona.h" |
| 26 | |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 27 | #ifdef CONFIG_ACPI |
Wei Yongjun | c4d0922 | 2021-02-10 07:56:26 +0000 | [diff] [blame] | 28 | static const struct acpi_gpio_params reset_gpios = { 1, 0, false }; |
| 29 | static const struct acpi_gpio_params ldoena_gpios = { 2, 0, false }; |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 30 | |
| 31 | static const struct acpi_gpio_mapping arizona_acpi_gpios[] = { |
| 32 | { "reset-gpios", &reset_gpios, 1, }, |
| 33 | { "wlf,ldoena-gpios", &ldoena_gpios, 1 }, |
| 34 | { } |
| 35 | }; |
| 36 | |
| 37 | /* |
| 38 | * The ACPI resources for the device only describe external GPIO-s. They do |
| 39 | * not provide mappings for the GPIO-s coming from the Arizona codec itself. |
| 40 | */ |
| 41 | static const struct gpiod_lookup arizona_soc_gpios[] = { |
| 42 | { "arizona", 2, "wlf,spkvdd-ena", 0, GPIO_ACTIVE_HIGH }, |
| 43 | { "arizona", 4, "wlf,micd-pol", 0, GPIO_ACTIVE_LOW }, |
| 44 | }; |
| 45 | |
| 46 | /* |
| 47 | * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: |
| 48 | * Function A Play/Pause: 0 ohm |
| 49 | * Function D Voice assistant: 135 ohm |
| 50 | * Function B Volume Up 240 ohm |
| 51 | * Function C Volume Down 470 ohm |
| 52 | * Minimum Mic DC resistance 1000 ohm |
| 53 | * Minimum Ear speaker impedance 16 ohm |
| 54 | * Note the first max value below must be less then the min. speaker impedance, |
| 55 | * to allow CTIA/OMTP detection to work. The other max values are the closest |
| 56 | * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. |
| 57 | */ |
| 58 | static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { |
| 59 | { .max = 11, .key = KEY_PLAYPAUSE }, |
| 60 | { .max = 186, .key = KEY_VOICECOMMAND }, |
| 61 | { .max = 348, .key = KEY_VOLUMEUP }, |
| 62 | { .max = 752, .key = KEY_VOLUMEDOWN }, |
| 63 | }; |
| 64 | |
| 65 | static void arizona_spi_acpi_remove_lookup(void *lookup) |
| 66 | { |
| 67 | gpiod_remove_lookup_table(lookup); |
| 68 | } |
| 69 | |
| 70 | static int arizona_spi_acpi_probe(struct arizona *arizona) |
| 71 | { |
| 72 | struct gpiod_lookup_table *lookup; |
| 73 | acpi_status status; |
| 74 | int ret; |
| 75 | |
| 76 | /* Add mappings for the 2 ACPI declared GPIOs used for reset and ldo-ena */ |
| 77 | devm_acpi_dev_add_driver_gpios(arizona->dev, arizona_acpi_gpios); |
| 78 | |
| 79 | /* Add lookups for the SoCs own GPIOs used for micdet-polarity and spkVDD-enable */ |
| 80 | lookup = devm_kzalloc(arizona->dev, |
| 81 | struct_size(lookup, table, ARRAY_SIZE(arizona_soc_gpios) + 1), |
| 82 | GFP_KERNEL); |
| 83 | if (!lookup) |
| 84 | return -ENOMEM; |
| 85 | |
| 86 | lookup->dev_id = dev_name(arizona->dev); |
| 87 | memcpy(lookup->table, arizona_soc_gpios, sizeof(arizona_soc_gpios)); |
| 88 | |
| 89 | gpiod_add_lookup_table(lookup); |
| 90 | ret = devm_add_action_or_reset(arizona->dev, arizona_spi_acpi_remove_lookup, lookup); |
| 91 | if (ret) |
| 92 | return ret; |
| 93 | |
| 94 | /* Enable 32KHz clock from SoC to codec for jack-detect */ |
| 95 | status = acpi_evaluate_object(ACPI_HANDLE(arizona->dev), "CLKE", NULL, NULL); |
| 96 | if (ACPI_FAILURE(status)) |
| 97 | dev_warn(arizona->dev, "Failed to enable 32KHz clk ACPI error %d\n", status); |
| 98 | |
| 99 | /* |
| 100 | * Some DSDTs wrongly declare the IRQ trigger-type as IRQF_TRIGGER_FALLING |
| 101 | * The IRQ line will stay low when a new IRQ event happens between reading |
| 102 | * the IRQ status flags and acknowledging them. When the IRQ line stays |
| 103 | * low like this the IRQ will never trigger again when its type is set |
| 104 | * to IRQF_TRIGGER_FALLING. Correct the IRQ trigger-type to fix this. |
| 105 | * |
| 106 | * Note theoretically it is possible that some boards are not capable |
| 107 | * of handling active low level interrupts. In that case setting the |
| 108 | * flag to IRQF_TRIGGER_FALLING would not be a bug (and we would need |
| 109 | * to work around this) but so far all known usages of IRQF_TRIGGER_FALLING |
| 110 | * are a bug in the board's DSDT. |
| 111 | */ |
| 112 | arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; |
| 113 | |
| 114 | /* Wait 200 ms after jack insertion */ |
| 115 | arizona->pdata.micd_detect_debounce = 200; |
| 116 | |
| 117 | /* Use standard AOSP values for headset-button mappings */ |
| 118 | arizona->pdata.micd_ranges = arizona_micd_aosp_ranges; |
| 119 | arizona->pdata.num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges); |
| 120 | |
| 121 | return 0; |
| 122 | } |
| 123 | |
| 124 | static const struct acpi_device_id arizona_acpi_match[] = { |
| 125 | { |
| 126 | .id = "WM510204", |
| 127 | .driver_data = WM5102, |
| 128 | }, |
| 129 | { |
| 130 | .id = "WM510205", |
| 131 | .driver_data = WM5102, |
| 132 | }, |
| 133 | { } |
| 134 | }; |
| 135 | MODULE_DEVICE_TABLE(acpi, arizona_acpi_match); |
| 136 | #else |
| 137 | static int arizona_spi_acpi_probe(struct arizona *arizona) |
| 138 | { |
| 139 | return -ENODEV; |
| 140 | } |
| 141 | #endif |
| 142 | |
Bill Pemberton | f791be4 | 2012-11-19 13:23:04 -0500 | [diff] [blame] | 143 | static int arizona_spi_probe(struct spi_device *spi) |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 144 | { |
| 145 | const struct spi_device_id *id = spi_get_device_id(spi); |
Hans de Goede | 039da22 | 2021-01-20 22:49:54 +0100 | [diff] [blame] | 146 | const void *match_data; |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 147 | struct arizona *arizona; |
Richard Fitzgerald | b61c1ec | 2015-10-02 13:29:14 +0100 | [diff] [blame] | 148 | const struct regmap_config *regmap_config = NULL; |
Hans de Goede | 039da22 | 2021-01-20 22:49:54 +0100 | [diff] [blame] | 149 | unsigned long type = 0; |
Lee Jones | 942786e | 2014-07-02 14:28:46 +0100 | [diff] [blame] | 150 | int ret; |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 151 | |
Hans de Goede | 039da22 | 2021-01-20 22:49:54 +0100 | [diff] [blame] | 152 | match_data = device_get_match_data(&spi->dev); |
| 153 | if (match_data) |
| 154 | type = (unsigned long)match_data; |
| 155 | else if (id) |
Mark Brown | d781009 | 2013-03-25 00:11:27 +0000 | [diff] [blame] | 156 | type = id->driver_data; |
| 157 | |
| 158 | switch (type) { |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 159 | case WM5102: |
Richard Fitzgerald | b61c1ec | 2015-10-02 13:29:14 +0100 | [diff] [blame] | 160 | if (IS_ENABLED(CONFIG_MFD_WM5102)) |
| 161 | regmap_config = &wm5102_spi_regmap; |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 162 | break; |
Mark Brown | e102bef | 2012-07-10 12:37:58 +0100 | [diff] [blame] | 163 | case WM5110: |
Richard Fitzgerald | e5d4ef0 | 2015-01-17 15:21:22 +0000 | [diff] [blame] | 164 | case WM8280: |
Richard Fitzgerald | b61c1ec | 2015-10-02 13:29:14 +0100 | [diff] [blame] | 165 | if (IS_ENABLED(CONFIG_MFD_WM5110)) |
| 166 | regmap_config = &wm5110_spi_regmap; |
Mark Brown | e102bef | 2012-07-10 12:37:58 +0100 | [diff] [blame] | 167 | break; |
Richard Fitzgerald | ea1f333 | 2015-11-03 15:08:32 +0000 | [diff] [blame] | 168 | case WM1831: |
| 169 | case CS47L24: |
| 170 | if (IS_ENABLED(CONFIG_MFD_CS47L24)) |
| 171 | regmap_config = &cs47l24_spi_regmap; |
| 172 | break; |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 173 | default: |
Richard Fitzgerald | 2e44e28 | 2015-10-02 13:29:15 +0100 | [diff] [blame] | 174 | dev_err(&spi->dev, "Unknown device type %ld\n", type); |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 175 | return -EINVAL; |
| 176 | } |
| 177 | |
Richard Fitzgerald | b61c1ec | 2015-10-02 13:29:14 +0100 | [diff] [blame] | 178 | if (!regmap_config) { |
| 179 | dev_err(&spi->dev, |
| 180 | "No kernel support for device type %ld\n", type); |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 181 | return -EINVAL; |
| 182 | } |
| 183 | |
| 184 | arizona = devm_kzalloc(&spi->dev, sizeof(*arizona), GFP_KERNEL); |
| 185 | if (arizona == NULL) |
| 186 | return -ENOMEM; |
| 187 | |
| 188 | arizona->regmap = devm_regmap_init_spi(spi, regmap_config); |
| 189 | if (IS_ERR(arizona->regmap)) { |
| 190 | ret = PTR_ERR(arizona->regmap); |
| 191 | dev_err(&spi->dev, "Failed to allocate register map: %d\n", |
| 192 | ret); |
| 193 | return ret; |
| 194 | } |
| 195 | |
Richard Fitzgerald | 2e44e28 | 2015-10-02 13:29:15 +0100 | [diff] [blame] | 196 | arizona->type = type; |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 197 | arizona->dev = &spi->dev; |
| 198 | arizona->irq = spi->irq; |
| 199 | |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 200 | if (has_acpi_companion(&spi->dev)) { |
| 201 | ret = arizona_spi_acpi_probe(arizona); |
| 202 | if (ret) |
| 203 | return ret; |
| 204 | } |
| 205 | |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 206 | return arizona_dev_init(arizona); |
| 207 | } |
| 208 | |
Bill Pemberton | 4740f73 | 2012-11-19 13:26:01 -0500 | [diff] [blame] | 209 | static int arizona_spi_remove(struct spi_device *spi) |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 210 | { |
Jingoo Han | e9642d5 | 2013-04-06 15:44:30 +0900 | [diff] [blame] | 211 | struct arizona *arizona = spi_get_drvdata(spi); |
Will Sheppard | 6f467e5 | 2014-10-15 09:38:47 +0100 | [diff] [blame] | 212 | |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 213 | arizona_dev_exit(arizona); |
Will Sheppard | 6f467e5 | 2014-10-15 09:38:47 +0100 | [diff] [blame] | 214 | |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | static const struct spi_device_id arizona_spi_ids[] = { |
| 219 | { "wm5102", WM5102 }, |
Mark Brown | e102bef | 2012-07-10 12:37:58 +0100 | [diff] [blame] | 220 | { "wm5110", WM5110 }, |
Richard Fitzgerald | e5d4ef0 | 2015-01-17 15:21:22 +0000 | [diff] [blame] | 221 | { "wm8280", WM8280 }, |
Richard Fitzgerald | ea1f333 | 2015-11-03 15:08:32 +0000 | [diff] [blame] | 222 | { "wm1831", WM1831 }, |
| 223 | { "cs47l24", CS47L24 }, |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 224 | { }, |
| 225 | }; |
| 226 | MODULE_DEVICE_TABLE(spi, arizona_spi_ids); |
| 227 | |
Charles Keepax | 3f65555 | 2021-09-28 17:30:35 +0100 | [diff] [blame] | 228 | #ifdef CONFIG_OF |
| 229 | const struct of_device_id arizona_spi_of_match[] = { |
| 230 | { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, |
| 231 | { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, |
| 232 | { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, |
| 233 | { .compatible = "wlf,wm1831", .data = (void *)WM1831 }, |
| 234 | { .compatible = "cirrus,cs47l24", .data = (void *)CS47L24 }, |
| 235 | {}, |
| 236 | }; |
| 237 | #endif |
| 238 | |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 239 | static struct spi_driver arizona_spi_driver = { |
| 240 | .driver = { |
| 241 | .name = "arizona", |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 242 | .pm = &arizona_pm_ops, |
Charles Keepax | 3f65555 | 2021-09-28 17:30:35 +0100 | [diff] [blame] | 243 | .of_match_table = of_match_ptr(arizona_spi_of_match), |
Hans de Goede | e9338367 | 2021-01-20 22:49:55 +0100 | [diff] [blame] | 244 | .acpi_match_table = ACPI_PTR(arizona_acpi_match), |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 245 | }, |
| 246 | .probe = arizona_spi_probe, |
Bill Pemberton | 8444921 | 2012-11-19 13:20:24 -0500 | [diff] [blame] | 247 | .remove = arizona_spi_remove, |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 248 | .id_table = arizona_spi_ids, |
| 249 | }; |
| 250 | |
| 251 | module_spi_driver(arizona_spi_driver); |
| 252 | |
Hans de Goede | 06e577b | 2021-01-20 22:49:53 +0100 | [diff] [blame] | 253 | MODULE_SOFTDEP("pre: arizona_ldo1"); |
Mark Brown | a15123c | 2012-06-19 16:36:18 +0100 | [diff] [blame] | 254 | MODULE_DESCRIPTION("Arizona SPI bus interface"); |
| 255 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); |
| 256 | MODULE_LICENSE("GPL"); |