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