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};
  50
  51static void do_nokia_modem_rst_ind_tasklet(unsigned long data)
  52{
  53        struct nokia_modem_device *modem = (struct nokia_modem_device *)data;
  54
  55        if (!modem)
  56                return;
  57
  58        dev_info(modem->device, "CMT rst line change detected\n");
  59
  60        if (modem->ssi_protocol)
  61                ssip_reset_event(modem->ssi_protocol);
  62}
  63
  64static irqreturn_t nokia_modem_rst_ind_isr(int irq, void *data)
  65{
  66        struct nokia_modem_device *modem = (struct nokia_modem_device *)data;
  67
  68        tasklet_schedule(&modem->nokia_modem_rst_ind_tasklet);
  69
  70        return IRQ_HANDLED;
  71}
  72
  73static void nokia_modem_gpio_unexport(struct device *dev)
  74{
  75        struct nokia_modem_device *modem = dev_get_drvdata(dev);
  76        int i;
  77
  78        for (i = 0; i < modem->gpio_amount; i++) {
  79                sysfs_remove_link(&dev->kobj, modem->gpios[i].name);
  80                gpiod_unexport(modem->gpios[i].gpio);
  81        }
  82}
  83
  84static int nokia_modem_gpio_probe(struct device *dev)
  85{
  86        struct device_node *np = dev->of_node;
  87        struct nokia_modem_device *modem = dev_get_drvdata(dev);
  88        int gpio_count, gpio_name_count, i, err;
  89
  90        gpio_count = of_gpio_count(np);
  91
  92        if (gpio_count < 0) {
  93                dev_err(dev, "missing gpios: %d\n", gpio_count);
  94                return gpio_count;
  95        }
  96
  97        gpio_name_count = of_property_count_strings(np, "gpio-names");
  98
  99        if (gpio_count != gpio_name_count) {
 100                dev_err(dev, "number of gpios does not equal number of gpio names\n");
 101                return -EINVAL;
 102        }
 103
 104        modem->gpios = devm_kzalloc(dev, gpio_count *
 105                                sizeof(struct nokia_modem_gpio), GFP_KERNEL);
 106        if (!modem->gpios) {
 107                dev_err(dev, "Could not allocate memory for gpios\n");
 108                return -ENOMEM;
 109        }
 110
 111        modem->gpio_amount = gpio_count;
 112
 113        for (i = 0; i < gpio_count; i++) {
 114                modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i);
 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_direction_output(modem->gpios[i].gpio, 0);
 128                if (err)
 129                        return err;
 130
 131                err = gpiod_export(modem->gpios[i].gpio, 0);
 132                if (err)
 133                        return err;
 134
 135                err = gpiod_export_link(dev, modem->gpios[i].name,
 136                                                        modem->gpios[i].gpio);
 137                if (err)
 138                        return err;
 139        }
 140
 141        return 0;
 142}
 143
 144static int nokia_modem_probe(struct device *dev)
 145{
 146        struct device_node *np;
 147        struct nokia_modem_device *modem;
 148        struct hsi_client *cl = to_hsi_client(dev);
 149        struct hsi_port *port = hsi_get_port(cl);
 150        int irq, pflags, err;
 151        struct hsi_board_info ssip;
 152
 153        np = dev->of_node;
 154        if (!np) {
 155                dev_err(dev, "device tree node not found\n");
 156                return -ENXIO;
 157        }
 158
 159        modem = devm_kzalloc(dev, sizeof(*modem), GFP_KERNEL);
 160        if (!modem) {
 161                dev_err(dev, "Could not allocate memory for nokia_modem_device\n");
 162                return -ENOMEM;
 163        }
 164        dev_set_drvdata(dev, modem);
 165        modem->device = dev;
 166
 167        irq = irq_of_parse_and_map(np, 0);
 168        if (!irq) {
 169                dev_err(dev, "Invalid rst_ind interrupt (%d)\n", irq);
 170                return -EINVAL;
 171        }
 172        modem->nokia_modem_rst_ind_irq = irq;
 173        pflags = irq_get_trigger_type(irq);
 174
 175        tasklet_init(&modem->nokia_modem_rst_ind_tasklet,
 176                        do_nokia_modem_rst_ind_tasklet, (unsigned long)modem);
 177        err = devm_request_irq(dev, irq, nokia_modem_rst_ind_isr,
 178                                pflags, "modem_rst_ind", modem);
 179        if (err < 0) {
 180                dev_err(dev, "Request rst_ind irq(%d) failed (flags %d)\n",
 181                                                                irq, pflags);
 182                return err;
 183        }
 184        enable_irq_wake(irq);
 185
 186        if(pm) {
 187                err = nokia_modem_gpio_probe(dev);
 188                if (err < 0) {
 189                        dev_err(dev, "Could not probe GPIOs\n");
 190                        goto error1;
 191                }
 192        }
 193
 194        ssip.name = "ssi-protocol";
 195        ssip.tx_cfg = cl->tx_cfg;
 196        ssip.rx_cfg = cl->rx_cfg;
 197        ssip.platform_data = NULL;
 198        ssip.archdata = NULL;
 199
 200        modem->ssi_protocol = hsi_new_client(port, &ssip);
 201        if (!modem->ssi_protocol) {
 202                dev_err(dev, "Could not register ssi-protocol device\n");
 203                goto error2;
 204        }
 205
 206        err = device_attach(&modem->ssi_protocol->device);
 207        if (err == 0) {
 208                dev_err(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        /* TODO: register cmt-speech hsi client */
 217
 218        dev_info(dev, "Registered Nokia HSI modem\n");
 219
 220        return 0;
 221
 222error3:
 223        hsi_remove_client(&modem->ssi_protocol->device, NULL);
 224error2:
 225        nokia_modem_gpio_unexport(dev);
 226error1:
 227        disable_irq_wake(modem->nokia_modem_rst_ind_irq);
 228        tasklet_kill(&modem->nokia_modem_rst_ind_tasklet);
 229
 230        return err;
 231}
 232
 233static int nokia_modem_remove(struct device *dev)
 234{
 235        struct nokia_modem_device *modem = dev_get_drvdata(dev);
 236
 237        if (!modem)
 238                return 0;
 239
 240        if (modem->ssi_protocol) {
 241                hsi_remove_client(&modem->ssi_protocol->device, NULL);
 242                modem->ssi_protocol = NULL;
 243        }
 244
 245        nokia_modem_gpio_unexport(dev);
 246        dev_set_drvdata(dev, NULL);
 247        disable_irq_wake(modem->nokia_modem_rst_ind_irq);
 248        tasklet_kill(&modem->nokia_modem_rst_ind_tasklet);
 249
 250        return 0;
 251}
 252
 253#ifdef CONFIG_OF
 254static const struct of_device_id nokia_modem_of_match[] = {
 255        { .compatible = "nokia,n900-modem", },
 256        {},
 257};
 258MODULE_DEVICE_TABLE(of, nokia_modem_of_match);
 259#endif
 260
 261static struct hsi_client_driver nokia_modem_driver = {
 262        .driver = {
 263                .name   = "nokia-modem",
 264                .owner  = THIS_MODULE,
 265                .probe  = nokia_modem_probe,
 266                .remove = nokia_modem_remove,
 267                .of_match_table = of_match_ptr(nokia_modem_of_match),
 268        },
 269};
 270
 271static int __init nokia_modem_init(void)
 272{
 273        return hsi_register_client_driver(&nokia_modem_driver);
 274}
 275module_init(nokia_modem_init);
 276
 277static void __exit nokia_modem_exit(void)
 278{
 279        hsi_unregister_client_driver(&nokia_modem_driver);
 280}
 281module_exit(nokia_modem_exit);
 282
 283MODULE_ALIAS("hsi:nokia-modem");
 284MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
 285MODULE_DESCRIPTION("HSI driver module for Nokia N900 Modem");
 286MODULE_LICENSE("GPL");
 287