nokia-modem.c (7207B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * nokia-modem.c 4 * 5 * HSI client driver for Nokia N900 modem. 6 * 7 * Copyright (C) 2014 Sebastian Reichel <sre@kernel.org> 8 */ 9 10#include <linux/gpio/consumer.h> 11#include <linux/hsi/hsi.h> 12#include <linux/init.h> 13#include <linux/interrupt.h> 14#include <linux/of.h> 15#include <linux/of_irq.h> 16#include <linux/of_gpio.h> 17#include <linux/hsi/ssi_protocol.h> 18 19static unsigned int pm = 1; 20module_param(pm, int, 0400); 21MODULE_PARM_DESC(pm, 22 "Enable power management (0=disabled, 1=userland based [default])"); 23 24struct nokia_modem_gpio { 25 struct gpio_desc *gpio; 26 const char *name; 27}; 28 29struct nokia_modem_device { 30 struct tasklet_struct nokia_modem_rst_ind_tasklet; 31 int nokia_modem_rst_ind_irq; 32 struct device *device; 33 struct nokia_modem_gpio *gpios; 34 int gpio_amount; 35 struct hsi_client *ssi_protocol; 36 struct hsi_client *cmt_speech; 37}; 38 39static void do_nokia_modem_rst_ind_tasklet(unsigned long data) 40{ 41 struct nokia_modem_device *modem = (struct nokia_modem_device *)data; 42 43 if (!modem) 44 return; 45 46 dev_info(modem->device, "CMT rst line change detected\n"); 47 48 if (modem->ssi_protocol) 49 ssip_reset_event(modem->ssi_protocol); 50} 51 52static irqreturn_t nokia_modem_rst_ind_isr(int irq, void *data) 53{ 54 struct nokia_modem_device *modem = (struct nokia_modem_device *)data; 55 56 tasklet_schedule(&modem->nokia_modem_rst_ind_tasklet); 57 58 return IRQ_HANDLED; 59} 60 61static void nokia_modem_gpio_unexport(struct device *dev) 62{ 63 struct nokia_modem_device *modem = dev_get_drvdata(dev); 64 int i; 65 66 for (i = 0; i < modem->gpio_amount; i++) { 67 sysfs_remove_link(&dev->kobj, modem->gpios[i].name); 68 gpiod_unexport(modem->gpios[i].gpio); 69 } 70} 71 72static int nokia_modem_gpio_probe(struct device *dev) 73{ 74 struct device_node *np = dev->of_node; 75 struct nokia_modem_device *modem = dev_get_drvdata(dev); 76 int gpio_count, gpio_name_count, i, err; 77 78 gpio_count = of_gpio_count(np); 79 80 if (gpio_count < 0) { 81 dev_err(dev, "missing gpios: %d\n", gpio_count); 82 return gpio_count; 83 } 84 85 gpio_name_count = of_property_count_strings(np, "gpio-names"); 86 87 if (gpio_count != gpio_name_count) { 88 dev_err(dev, "number of gpios does not equal number of gpio names\n"); 89 return -EINVAL; 90 } 91 92 modem->gpios = devm_kcalloc(dev, gpio_count, sizeof(*modem->gpios), 93 GFP_KERNEL); 94 if (!modem->gpios) 95 return -ENOMEM; 96 97 modem->gpio_amount = gpio_count; 98 99 for (i = 0; i < gpio_count; i++) { 100 modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i, 101 GPIOD_OUT_LOW); 102 if (IS_ERR(modem->gpios[i].gpio)) { 103 dev_err(dev, "Could not get gpio %d\n", i); 104 return PTR_ERR(modem->gpios[i].gpio); 105 } 106 107 err = of_property_read_string_index(np, "gpio-names", i, 108 &(modem->gpios[i].name)); 109 if (err) { 110 dev_err(dev, "Could not get gpio name %d\n", i); 111 return err; 112 } 113 114 err = gpiod_export(modem->gpios[i].gpio, 0); 115 if (err) 116 return err; 117 118 err = gpiod_export_link(dev, modem->gpios[i].name, 119 modem->gpios[i].gpio); 120 if (err) 121 return err; 122 } 123 124 return 0; 125} 126 127static int nokia_modem_probe(struct device *dev) 128{ 129 struct device_node *np; 130 struct nokia_modem_device *modem; 131 struct hsi_client *cl = to_hsi_client(dev); 132 struct hsi_port *port = hsi_get_port(cl); 133 int irq, pflags, err; 134 struct hsi_board_info ssip; 135 struct hsi_board_info cmtspeech; 136 137 np = dev->of_node; 138 if (!np) { 139 dev_err(dev, "device tree node not found\n"); 140 return -ENXIO; 141 } 142 143 modem = devm_kzalloc(dev, sizeof(*modem), GFP_KERNEL); 144 if (!modem) 145 return -ENOMEM; 146 147 dev_set_drvdata(dev, modem); 148 modem->device = dev; 149 150 irq = irq_of_parse_and_map(np, 0); 151 if (!irq) { 152 dev_err(dev, "Invalid rst_ind interrupt (%d)\n", irq); 153 return -EINVAL; 154 } 155 modem->nokia_modem_rst_ind_irq = irq; 156 pflags = irq_get_trigger_type(irq); 157 158 tasklet_init(&modem->nokia_modem_rst_ind_tasklet, 159 do_nokia_modem_rst_ind_tasklet, (unsigned long)modem); 160 err = devm_request_irq(dev, irq, nokia_modem_rst_ind_isr, 161 pflags, "modem_rst_ind", modem); 162 if (err < 0) { 163 dev_err(dev, "Request rst_ind irq(%d) failed (flags %d)\n", 164 irq, pflags); 165 return err; 166 } 167 enable_irq_wake(irq); 168 169 if (pm) { 170 err = nokia_modem_gpio_probe(dev); 171 if (err < 0) { 172 dev_err(dev, "Could not probe GPIOs\n"); 173 goto error1; 174 } 175 } 176 177 ssip.name = "ssi-protocol"; 178 ssip.tx_cfg = cl->tx_cfg; 179 ssip.rx_cfg = cl->rx_cfg; 180 ssip.platform_data = NULL; 181 ssip.archdata = NULL; 182 183 modem->ssi_protocol = hsi_new_client(port, &ssip); 184 if (!modem->ssi_protocol) { 185 dev_err(dev, "Could not register ssi-protocol device\n"); 186 err = -ENOMEM; 187 goto error2; 188 } 189 190 err = device_attach(&modem->ssi_protocol->device); 191 if (err == 0) { 192 dev_dbg(dev, "Missing ssi-protocol driver\n"); 193 err = -EPROBE_DEFER; 194 goto error3; 195 } else if (err < 0) { 196 dev_err(dev, "Could not load ssi-protocol driver (%d)\n", err); 197 goto error3; 198 } 199 200 cmtspeech.name = "cmt-speech"; 201 cmtspeech.tx_cfg = cl->tx_cfg; 202 cmtspeech.rx_cfg = cl->rx_cfg; 203 cmtspeech.platform_data = NULL; 204 cmtspeech.archdata = NULL; 205 206 modem->cmt_speech = hsi_new_client(port, &cmtspeech); 207 if (!modem->cmt_speech) { 208 dev_err(dev, "Could not register cmt-speech device\n"); 209 err = -ENOMEM; 210 goto error3; 211 } 212 213 err = device_attach(&modem->cmt_speech->device); 214 if (err == 0) { 215 dev_dbg(dev, "Missing cmt-speech driver\n"); 216 err = -EPROBE_DEFER; 217 goto error4; 218 } else if (err < 0) { 219 dev_err(dev, "Could not load cmt-speech driver (%d)\n", err); 220 goto error4; 221 } 222 223 dev_info(dev, "Registered Nokia HSI modem\n"); 224 225 return 0; 226 227error4: 228 hsi_remove_client(&modem->cmt_speech->device, NULL); 229error3: 230 hsi_remove_client(&modem->ssi_protocol->device, NULL); 231error2: 232 nokia_modem_gpio_unexport(dev); 233error1: 234 disable_irq_wake(modem->nokia_modem_rst_ind_irq); 235 tasklet_kill(&modem->nokia_modem_rst_ind_tasklet); 236 237 return err; 238} 239 240static int nokia_modem_remove(struct device *dev) 241{ 242 struct nokia_modem_device *modem = dev_get_drvdata(dev); 243 244 if (!modem) 245 return 0; 246 247 if (modem->cmt_speech) { 248 hsi_remove_client(&modem->cmt_speech->device, NULL); 249 modem->cmt_speech = NULL; 250 } 251 252 if (modem->ssi_protocol) { 253 hsi_remove_client(&modem->ssi_protocol->device, NULL); 254 modem->ssi_protocol = NULL; 255 } 256 257 nokia_modem_gpio_unexport(dev); 258 dev_set_drvdata(dev, NULL); 259 disable_irq_wake(modem->nokia_modem_rst_ind_irq); 260 tasklet_kill(&modem->nokia_modem_rst_ind_tasklet); 261 262 return 0; 263} 264 265#ifdef CONFIG_OF 266static const struct of_device_id nokia_modem_of_match[] = { 267 { .compatible = "nokia,n900-modem", }, 268 { .compatible = "nokia,n950-modem", }, 269 { .compatible = "nokia,n9-modem", }, 270 {}, 271}; 272MODULE_DEVICE_TABLE(of, nokia_modem_of_match); 273#endif 274 275static struct hsi_client_driver nokia_modem_driver = { 276 .driver = { 277 .name = "nokia-modem", 278 .owner = THIS_MODULE, 279 .probe = nokia_modem_probe, 280 .remove = nokia_modem_remove, 281 .of_match_table = of_match_ptr(nokia_modem_of_match), 282 }, 283}; 284 285static int __init nokia_modem_init(void) 286{ 287 return hsi_register_client_driver(&nokia_modem_driver); 288} 289module_init(nokia_modem_init); 290 291static void __exit nokia_modem_exit(void) 292{ 293 hsi_unregister_client_driver(&nokia_modem_driver); 294} 295module_exit(nokia_modem_exit); 296 297MODULE_ALIAS("hsi:nokia-modem"); 298MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); 299MODULE_DESCRIPTION("HSI driver module for Nokia N900 Modem"); 300MODULE_LICENSE("GPL");