linux/drivers/hsi/clients/nokia-modem.c
<<
>>
Prefs
   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");
 301