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_kzalloc(dev, gpio_count *
 106                                sizeof(struct nokia_modem_gpio), GFP_KERNEL);
 107        if (!modem->gpios) {
 108                dev_err(dev, "Could not allocate memory for gpios\n");
 109                return -ENOMEM;
 110        }
 111
 112        modem->gpio_amount = gpio_count;
 113
 114        for (i = 0; i < gpio_count; i++) {
 115                modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i,
 116                                                            GPIOD_OUT_LOW);
 117                if (IS_ERR(modem->gpios[i].gpio)) {
 118                        dev_err(dev, "Could not get gpio %d\n", i);
 119                        return PTR_ERR(modem->gpios[i].gpio);
 120                }
 121
 122                err = of_property_read_string_index(np, "gpio-names", i,
 123                                                &(modem->gpios[i].name));
 124                if (err) {
 125                        dev_err(dev, "Could not get gpio name %d\n", i);
 126                        return err;
 127                }
 128
 129                err = gpiod_export(modem->gpios[i].gpio, 0);
 130                if (err)
 131                        return err;
 132
 133                err = gpiod_export_link(dev, modem->gpios[i].name,
 134                                                        modem->gpios[i].gpio);
 135                if (err)
 136                        return err;
 137        }
 138
 139        return 0;
 140}
 141
 142static int nokia_modem_probe(struct device *dev)
 143{
 144        struct device_node *np;
 145        struct nokia_modem_device *modem;
 146        struct hsi_client *cl = to_hsi_client(dev);
 147        struct hsi_port *port = hsi_get_port(cl);
 148        int irq, pflags, err;
 149        struct hsi_board_info ssip;
 150        struct hsi_board_info cmtspeech;
 151
 152        np = dev->of_node;
 153        if (!np) {
 154                dev_err(dev, "device tree node not found\n");
 155                return -ENXIO;
 156        }
 157
 158        modem = devm_kzalloc(dev, sizeof(*modem), GFP_KERNEL);
 159        if (!modem) {
 160                dev_err(dev, "Could not allocate memory for nokia_modem_device\n");
 161                return -ENOMEM;
 162        }
 163        dev_set_drvdata(dev, modem);
 164        modem->device = dev;
 165
 166        irq = irq_of_parse_and_map(np, 0);
 167        if (!irq) {
 168                dev_err(dev, "Invalid rst_ind interrupt (%d)\n", irq);
 169                return -EINVAL;
 170        }
 171        modem->nokia_modem_rst_ind_irq = irq;
 172        pflags = irq_get_trigger_type(irq);
 173
 174        tasklet_init(&modem->nokia_modem_rst_ind_tasklet,
 175                        do_nokia_modem_rst_ind_tasklet, (unsigned long)modem);
 176        err = devm_request_irq(dev, irq, nokia_modem_rst_ind_isr,
 177                                pflags, "modem_rst_ind", modem);
 178        if (err < 0) {
 179                dev_err(dev, "Request rst_ind irq(%d) failed (flags %d)\n",
 180                                                                irq, pflags);
 181                return err;
 182        }
 183        enable_irq_wake(irq);
 184
 185        if(pm) {
 186                err = nokia_modem_gpio_probe(dev);
 187                if (err < 0) {
 188                        dev_err(dev, "Could not probe GPIOs\n");
 189                        goto error1;
 190                }
 191        }
 192
 193        ssip.name = "ssi-protocol";
 194        ssip.tx_cfg = cl->tx_cfg;
 195        ssip.rx_cfg = cl->rx_cfg;
 196        ssip.platform_data = NULL;
 197        ssip.archdata = NULL;
 198
 199        modem->ssi_protocol = hsi_new_client(port, &ssip);
 200        if (!modem->ssi_protocol) {
 201                dev_err(dev, "Could not register ssi-protocol device\n");
 202                err = -ENOMEM;
 203                goto error2;
 204        }
 205
 206        err = device_attach(&modem->ssi_protocol->device);
 207        if (err == 0) {
 208                dev_dbg(dev, "Missing ssi-protocol driver\n");
 209                err = -EPROBE_DEFER;
 210                goto error3;
 211        } else if (err < 0) {
 212                dev_err(dev, "Could not load ssi-protocol driver (%d)\n", err);
 213                goto error3;
 214        }
 215
 216        cmtspeech.name = "cmt-speech";
 217        cmtspeech.tx_cfg = cl->tx_cfg;
 218        cmtspeech.rx_cfg = cl->rx_cfg;
 219        cmtspeech.platform_data = NULL;
 220        cmtspeech.archdata = NULL;
 221
 222        modem->cmt_speech = hsi_new_client(port, &cmtspeech);
 223        if (!modem->cmt_speech) {
 224                dev_err(dev, "Could not register cmt-speech device\n");
 225                err = -ENOMEM;
 226                goto error3;
 227        }
 228
 229        err = device_attach(&modem->cmt_speech->device);
 230        if (err == 0) {
 231                dev_dbg(dev, "Missing cmt-speech driver\n");
 232                err = -EPROBE_DEFER;
 233                goto error4;
 234        } else if (err < 0) {
 235                dev_err(dev, "Could not load cmt-speech driver (%d)\n", err);
 236                goto error4;
 237        }
 238
 239        dev_info(dev, "Registered Nokia HSI modem\n");
 240
 241        return 0;
 242
 243error4:
 244        hsi_remove_client(&modem->cmt_speech->device, NULL);
 245error3:
 246        hsi_remove_client(&modem->ssi_protocol->device, NULL);
 247error2:
 248        nokia_modem_gpio_unexport(dev);
 249error1:
 250        disable_irq_wake(modem->nokia_modem_rst_ind_irq);
 251        tasklet_kill(&modem->nokia_modem_rst_ind_tasklet);
 252
 253        return err;
 254}
 255
 256static int nokia_modem_remove(struct device *dev)
 257{
 258        struct nokia_modem_device *modem = dev_get_drvdata(dev);
 259
 260        if (!modem)
 261                return 0;
 262
 263        if (modem->cmt_speech) {
 264                hsi_remove_client(&modem->cmt_speech->device, NULL);
 265                modem->cmt_speech = NULL;
 266        }
 267
 268        if (modem->ssi_protocol) {
 269                hsi_remove_client(&modem->ssi_protocol->device, NULL);
 270                modem->ssi_protocol = NULL;
 271        }
 272
 273        nokia_modem_gpio_unexport(dev);
 274        dev_set_drvdata(dev, NULL);
 275        disable_irq_wake(modem->nokia_modem_rst_ind_irq);
 276        tasklet_kill(&modem->nokia_modem_rst_ind_tasklet);
 277
 278        return 0;
 279}
 280
 281#ifdef CONFIG_OF
 282static const struct of_device_id nokia_modem_of_match[] = {
 283        { .compatible = "nokia,n900-modem", },
 284        { .compatible = "nokia,n950-modem", },
 285        { .compatible = "nokia,n9-modem", },
 286        {},
 287};
 288MODULE_DEVICE_TABLE(of, nokia_modem_of_match);
 289#endif
 290
 291static struct hsi_client_driver nokia_modem_driver = {
 292        .driver = {
 293                .name   = "nokia-modem",
 294                .owner  = THIS_MODULE,
 295                .probe  = nokia_modem_probe,
 296                .remove = nokia_modem_remove,
 297                .of_match_table = of_match_ptr(nokia_modem_of_match),
 298        },
 299};
 300
 301static int __init nokia_modem_init(void)
 302{
 303        return hsi_register_client_driver(&nokia_modem_driver);
 304}
 305module_init(nokia_modem_init);
 306
 307static void __exit nokia_modem_exit(void)
 308{
 309        hsi_unregister_client_driver(&nokia_modem_driver);
 310}
 311module_exit(nokia_modem_exit);
 312
 313MODULE_ALIAS("hsi:nokia-modem");
 314MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
 315MODULE_DESCRIPTION("HSI driver module for Nokia N900 Modem");
 316MODULE_LICENSE("GPL");
 317