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