| /* linux/drivers/modem/modem.c |
| * |
| * Copyright (C) 2010 Google, Inc. |
| * Copyright (C) 2010 Samsung Electronics. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/interrupt.h> |
| #include <linux/platform_device.h> |
| #include <linux/miscdevice.h> |
| |
| #include <linux/uaccess.h> |
| #include <linux/fs.h> |
| #include <linux/io.h> |
| #include <linux/wait.h> |
| #include <linux/sched.h> |
| #include <linux/slab.h> |
| #include <linux/mutex.h> |
| #include <linux/irq.h> |
| #include <linux/gpio.h> |
| #include <linux/delay.h> |
| #include <linux/wakelock.h> |
| |
| #include <linux/platform_data/modem.h> |
| #include "modem_prj.h" |
| #include "modem_variation.h" |
| |
| |
| static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) |
| { |
| int ret = 0; |
| struct modem_data *pdata; |
| struct modem_ctl *modemctl; |
| struct device *dev = &pdev->dev; |
| |
| /* create modem control device */ |
| modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL); |
| if (!modemctl) |
| return NULL; |
| |
| modemctl->dev = dev; |
| modemctl->phone_state = STATE_OFFLINE; |
| |
| pdata = pdev->dev.platform_data; |
| modemctl->name = pdata->name; |
| |
| /* init modemctl device for getting modemctl operations */ |
| ret = call_modem_init_func(modemctl, pdata); |
| if (ret) { |
| kfree(modemctl); |
| return NULL; |
| } |
| |
| pr_debug("[MODEM_IF] %s:create_modemctl_device DONE\n", modemctl->name); |
| return modemctl; |
| } |
| |
| static struct io_device *create_io_device(struct modem_io_t *io_t, |
| struct modem_ctl *modemctl, enum modem_network modem_net) |
| { |
| int ret = 0; |
| struct io_device *iod = NULL; |
| |
| iod = kzalloc(sizeof(struct io_device), GFP_KERNEL); |
| if (!iod) { |
| pr_err("[MODEM_IF] io device memory alloc fail\n"); |
| return NULL; |
| } |
| |
| iod->name = io_t->name; |
| iod->id = io_t->id; |
| iod->format = io_t->format; |
| iod->io_typ = io_t->io_type; |
| iod->net_typ = modem_net; |
| |
| /* link between io device and modem control */ |
| iod->mc = modemctl; |
| if (iod->format == IPC_FMT) |
| modemctl->iod = iod; |
| |
| /* register misc device or net device */ |
| ret = init_io_device(iod); |
| if (ret) { |
| kfree(iod); |
| return NULL; |
| } |
| |
| pr_debug("[MODEM_IF] %s : create_io_device DONE\n", io_t->name); |
| return iod; |
| } |
| |
| static int __devinit modem_probe(struct platform_device *pdev) |
| { |
| int i; |
| struct modem_data *pdata; |
| struct modem_ctl *modemctl; |
| struct io_device *iod[MAX_NUM_IO_DEV]; |
| struct link_device *ld; |
| struct io_raw_devices *io_raw_devs = NULL; |
| |
| pdata = pdev->dev.platform_data; |
| memset(iod, 0, sizeof(iod)); |
| |
| modemctl = create_modemctl_device(pdev); |
| if (!modemctl) |
| return -ENOMEM; |
| |
| /* create link device */ |
| ld = call_link_init_func(pdev, pdata->link_type); |
| if (!ld) |
| goto err_free_modemctl; |
| |
| io_raw_devs = kzalloc(sizeof(struct io_raw_devices), GFP_KERNEL); |
| if (!io_raw_devs) |
| return -ENOMEM; |
| |
| /* create io deivces and connect to modemctl device */ |
| for (i = 0; i < pdata->num_iodevs; i++) { |
| iod[i] = create_io_device(&pdata->iodevs[i], modemctl, |
| pdata->modem_net); |
| if (!iod[i]) |
| goto err_free_modemctl; |
| |
| if (iod[i]->format == IPC_RAW) { |
| int ch = iod[i]->id & 0x1F; |
| io_raw_devs->raw_devices[ch] = iod[i]; |
| io_raw_devs->num_of_raw_devs++; |
| iod[i]->link = ld; |
| } else { |
| /* connect io devices to one link device */ |
| ld->attach(ld, iod[i]); |
| } |
| |
| if (iod[i]->format == IPC_MULTI_RAW) |
| iod[i]->private_data = (void *)io_raw_devs; |
| } |
| |
| platform_set_drvdata(pdev, modemctl); |
| |
| pr_debug("[MODEM_IF] modem_probe DONE\n"); |
| return 0; |
| |
| err_free_modemctl: |
| for (i = 0; i < pdata->num_iodevs; i++) |
| if (iod[i] != NULL) |
| kfree(iod[i]); |
| |
| if (io_raw_devs != NULL) |
| kfree(io_raw_devs); |
| |
| if (modemctl != NULL) |
| kfree(modemctl); |
| |
| return -ENOMEM; |
| } |
| |
| static void modem_shutdown(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct modem_ctl *mc = dev_get_drvdata(dev); |
| |
| if (!mc) |
| return; |
| |
| free_irq(mc->irq_phone_active, mc); |
| |
| if (mc->ops.modem_off) |
| mc->ops.modem_off(mc); |
| } |
| |
| static int modem_suspend(struct device *pdev) |
| { |
| struct modem_ctl *mc = dev_get_drvdata(pdev); |
| gpio_set_value(mc->gpio_pda_active, 0); |
| return 0; |
| } |
| |
| static int modem_resume(struct device *pdev) |
| { |
| struct modem_ctl *mc = dev_get_drvdata(pdev); |
| gpio_set_value(mc->gpio_pda_active, 1); |
| return 0; |
| } |
| |
| static const struct dev_pm_ops modem_pm_ops = { |
| .suspend = modem_suspend, |
| .resume = modem_resume, |
| }; |
| |
| static struct platform_driver modem_driver = { |
| .probe = modem_probe, |
| .shutdown = modem_shutdown, |
| .driver = { |
| .name = "modem_if", |
| .pm = &modem_pm_ops, |
| }, |
| }; |
| |
| static int __init modem_init(void) |
| { |
| return platform_driver_register(&modem_driver); |
| } |
| |
| module_init(modem_init); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("Samsung Modem Interface Driver"); |