diff --git a/Documentation/devicetree/bindings/net/4g-modem-platdata.txt b/Documentation/devicetree/bindings/net/4g-modem-platdata.txt new file mode 100644 index 000000000000..413c0793357f --- /dev/null +++ b/Documentation/devicetree/bindings/net/4g-modem-platdata.txt @@ -0,0 +1,18 @@ +* Rockchip 4g modem device tree bindings + +Required properties: +- compatible : "4g-modem-platdata" + +Optional properties: +- 4G,vbat-gpio : 4g modem vbat gpio +- 4G,power-gpio : 4g modem power enable/disable gpio +- 4G,reset-gpio : 4g modem reset gpio + +Example: + + 4G-Modem { + compatible="4g-modem-platdata"; + 4G,vbat-gpio = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>; + 4G,power-gpio = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; + 4G,reset-gpio = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>; + } diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 619bf1498a66..06ec56a5485b 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -498,6 +498,7 @@ config THUNDERBOLT_NET called thunderbolt-net. source "drivers/net/hyperv/Kconfig" +source "drivers/net/lte/Kconfig" config NETDEVSIM tristate "Simulated networking device" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 0d3ba056cda3..ccd0345ae83e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_HYPERV_NET) += hyperv/ obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o obj-$(CONFIG_FUJITSU_ES) += fjes/ +obj-$(CONFIG_LTE) += lte/ thunderbolt-net-y += thunderbolt.o obj-$(CONFIG_THUNDERBOLT_NET) += thunderbolt-net.o diff --git a/drivers/net/lte/Kconfig b/drivers/net/lte/Kconfig new file mode 100644 index 000000000000..fc36bce484af --- /dev/null +++ b/drivers/net/lte/Kconfig @@ -0,0 +1,15 @@ +# +# LTE network device configuration +# + +menuconfig LTE + bool "Rockchip LTE support" + help + Enable LTE drivers for Rockchip platform. + +if LTE +config LTE_RM310 + bool "LTE rm310 support" + default n + +endif diff --git a/drivers/net/lte/Makefile b/drivers/net/lte/Makefile new file mode 100644 index 000000000000..f6adba4338c7 --- /dev/null +++ b/drivers/net/lte/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Linux network LTE device drivers. +# + +obj-$(CONFIG_LTE_RM310) += lte_rm310.o diff --git a/drivers/net/lte/lte_rm310.c b/drivers/net/lte/lte_rm310.c new file mode 100644 index 000000000000..37d1ab9665df --- /dev/null +++ b/drivers/net/lte/lte_rm310.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd. + * + * Rockchip rm310 driver for 4g modem + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG(x...) pr_info("[4g_modem]: " x) + +static struct lte_data *gpdata; +static struct class *modem_class; +static int modem_status = 1; + +static int modem_poweron_off(int on_off) +{ + struct lte_data *pdata = gpdata; + + if (pdata) { + if (on_off) { + LOG("%s: 4g modem power up.\n", __func__); + if (pdata->vbat_gpio) { + gpiod_direction_output(pdata->vbat_gpio, 1); + msleep(2000); + } + if (pdata->power_gpio) { + gpiod_direction_output(pdata->power_gpio, 0); + msleep(50); + gpiod_direction_output(pdata->power_gpio, 1); + msleep(400); + gpiod_direction_output(pdata->power_gpio, 0); + } + } else { + LOG("%s: 4g modem power down.\n", __func__); + if (pdata->power_gpio) { + gpiod_direction_output(pdata->power_gpio, 0); + msleep(100); + gpiod_direction_output(pdata->power_gpio, 1); + msleep(1000); + gpiod_direction_output(pdata->power_gpio, 0); + msleep(400); + } + if (pdata->vbat_gpio) + gpiod_direction_output(pdata->vbat_gpio, 0); + } + } + return 0; +} + +static ssize_t modem_status_store(struct class *cls, + struct class_attribute *attr, + const char *buf, size_t count) +{ + int new_state, ret; + + ret = kstrtoint(buf, 10, &new_state); + if (ret) { + LOG("%s: kstrtoint error return %d\n", __func__, ret); + return ret; + } + if (new_state == modem_status) + return count; + if (new_state == 1) { + LOG("%s, c(%d), open modem.\n", __func__, new_state); + modem_poweron_off(1); + } else if (new_state == 0) { + LOG("%s, c(%d), close modem.\n", __func__, new_state); + modem_poweron_off(0); + } else { + LOG("%s, invalid parameter.\n", __func__); + } + modem_status = new_state; + return count; +} + +static ssize_t modem_status_show(struct class *cls, + struct class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", modem_status); +} + +static CLASS_ATTR_RW(modem_status); + +static int modem_platdata_parse_dt(struct device *dev, + struct lte_data *data) +{ + struct device_node *node = dev->of_node; + int ret; + + if (!node) + return -ENODEV; + memset(data, 0, sizeof(*data)); + LOG("%s: LTE modem power controlled by gpio.\n", __func__); + data->vbat_gpio = devm_gpiod_get_optional(dev, "4G,vbat", + GPIOD_OUT_HIGH); + if (IS_ERR(data->vbat_gpio)) { + ret = PTR_ERR(data->vbat_gpio); + dev_err(dev, "failed to request 4G,vbat GPIO: %d\n", ret); + return ret; + } + data->power_gpio = devm_gpiod_get_optional(dev, "4G,power", + GPIOD_OUT_HIGH); + if (IS_ERR(data->power_gpio)) { + ret = PTR_ERR(data->power_gpio); + dev_err(dev, "failed to request 4G,power GPIO: %d\n", ret); + return ret; + } + data->reset_gpio = devm_gpiod_get_optional(dev, "4G,reset", + GPIOD_OUT_LOW); + if (IS_ERR(data->reset_gpio)) { + ret = PTR_ERR(data->reset_gpio); + dev_err(dev, "failed to request 4G,reset GPIO: %d\n", ret); + return ret; + } + return 0; +} + +static int modem_power_on_thread(void *data) +{ + modem_poweron_off(1); + return 0; +} + +static int lte_probe(struct platform_device *pdev) +{ + struct lte_data *pdata; + struct task_struct *kthread; + int ret = -1; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + ret = modem_platdata_parse_dt(&pdev->dev, pdata); + if (ret < 0) { + LOG("%s: No lte platform data specified\n", __func__); + goto err; + } + gpdata = pdata; + pdata->dev = &pdev->dev; + + if (pdata->reset_gpio) + gpiod_direction_output(pdata->reset_gpio, 0); + kthread = kthread_run(modem_power_on_thread, NULL, + "modem_power_on_thread"); + if (IS_ERR(kthread)) { + LOG("%s: create modem_power_on_thread failed.\n", __func__); + ret = PTR_ERR(kthread); + goto err; + } + return 0; +err: + gpdata = NULL; + return ret; +} + +static int lte_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int lte_resume(struct platform_device *pdev) +{ + return 0; +} + +static int lte_remove(struct platform_device *pdev) +{ + struct lte_data *pdata = gpdata; + + if (pdata->power_gpio) { + msleep(100); + gpiod_direction_output(pdata->power_gpio, 1); + msleep(750); + gpiod_direction_output(pdata->power_gpio, 0); + } + if (pdata->vbat_gpio) + gpiod_direction_output(pdata->vbat_gpio, 0); + gpdata = NULL; + return 0; +} + +static const struct of_device_id modem_platdata_of_match[] = { + { .compatible = "4g-modem-platdata" }, + { } +}; +MODULE_DEVICE_TABLE(of, modem_platdata_of_match); + +static struct platform_driver rm310_driver = { + .probe = lte_probe, + .remove = lte_remove, + .suspend = lte_suspend, + .resume = lte_resume, + .driver = { + .name = "4g-modem-platdata", + .of_match_table = of_match_ptr(modem_platdata_of_match), + }, +}; + +static int __init rm310_init(void) +{ + int ret; + + modem_class = class_create(THIS_MODULE, "rk_modem"); + ret = class_create_file(modem_class, &class_attr_modem_status); + if (ret) + LOG("Fail to create class modem_status.\n"); + return platform_driver_register(&rm310_driver); +} + +static void __exit rm310_exit(void) +{ + platform_driver_unregister(&rm310_driver); +} + +late_initcall(rm310_init); +module_exit(rm310_exit); + +MODULE_AUTHOR("xuxuehui "); +MODULE_AUTHOR("Alex Zhao "); +MODULE_DESCRIPTION("RM310 lte modem driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/lte.h b/include/linux/lte.h new file mode 100644 index 000000000000..3c9741df54b0 --- /dev/null +++ b/include/linux/lte.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LTE_H +#define __LTE_H + +#include +#include +#include + +struct lte_data { + struct device *dev; + struct gpio_desc *reset_gpio; + struct gpio_desc *power_gpio; + struct gpio_desc *vbat_gpio; +}; + +#endif /* __LTE_H */