nt9856x/BSP/linux-kernel/drivers/pinctrl/novatek/pinctrl-nvt-plat.c
2023-03-28 15:07:53 +08:00

288 lines
6.7 KiB
C
Executable File

/*
* Driver for the Novatek pinmux
*
* Copyright (c) 2019, NOVATEK MICROELECTRONIC CORPORATION. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "nvt_pinmux.h"
u32 top_reg_addr = 0;
extern u32 top_reg_addr;
static PINMUX_FUNC_ID id_restore = 0x0;
static u32 pinmux_restore = 0x0;
static void nvt_pinmux_show_conflict(struct nvt_pinctrl_info *info)
{
int i;
pinmux_parsing(info);
for (i = 0; i < PIN_FUNC_MAX; i++) {
pr_err("pinmux %-2d config 0x%x\n", i, info->top_pinmux[i].config);
}
panic("###### Conflicted Pinmux Setting ######\n");
}
int nvt_pinmux_capture(PIN_GROUP_CONFIG *pinmux_config, int count)
{
struct nvt_pinctrl_info *info;
int i, j, ret = E_OK;
if (in_interrupt() || in_atomic() || irqs_disabled())
info = kmalloc(sizeof(struct nvt_pinctrl_info), GFP_ATOMIC);
else
info = kmalloc(sizeof(struct nvt_pinctrl_info), GFP_KERNEL);
if (!info) {
pr_err("nvt pinmux mem alloc fail\n");
return -ENOMEM;
}
if (top_reg_addr) {
info->top_base = (void*) top_reg_addr;
pinmux_parsing(info);
for (j = 0; j < count; j++) {
for (i = 0; i < PIN_FUNC_MAX; i++) {
if (i == pinmux_config[j].pin_function)
pinmux_config[j].config = info->top_pinmux[i].config;
}
}
} else {
pr_err("invalid pinmux address\n");
ret = -ENOMEM;
}
kfree(info);
return ret;
}
EXPORT_SYMBOL(nvt_pinmux_capture);
int nvt_pinmux_update(PIN_GROUP_CONFIG *pinmux_config, int count)
{
struct nvt_pinctrl_info *info;
int i, j, ret = E_OK;
if (in_interrupt() || in_atomic() || irqs_disabled())
info = kmalloc(sizeof(struct nvt_pinctrl_info), GFP_ATOMIC);
else
info = kmalloc(sizeof(struct nvt_pinctrl_info), GFP_KERNEL);
if (!info) {
pr_err("nvt pinmux mem alloc fail\n");
return -ENOMEM;
}
if (top_reg_addr) {
info->top_base = (void*) top_reg_addr;
pinmux_parsing(info);
for (j = 0; j < count; j++) {
for (i = 0; i < PIN_FUNC_MAX; i++) {
if (i == pinmux_config[j].pin_function)
info->top_pinmux[i].config = pinmux_config[j].config;
}
}
ret = pinmux_init(info);
if (ret == E_OK)
ret = pinmux_set_config(id_restore, pinmux_restore);
} else {
pr_err("invalid pinmux address\n");
ret = -ENOMEM;
}
kfree(info);
return ret;
}
EXPORT_SYMBOL(nvt_pinmux_update);
int pinmux_set_config(PINMUX_FUNC_ID id, u32 pinmux)
{
struct nvt_pinctrl_info *info;
int ret;
if (in_interrupt() || in_atomic() || irqs_disabled())
info = kmalloc(sizeof(struct nvt_pinctrl_info), GFP_ATOMIC);
else
info = kmalloc(sizeof(struct nvt_pinctrl_info), GFP_KERNEL);
if (!info) {
pr_err("nvt pinmux mem alloc fail\n");
return -ENOMEM;
}
if (top_reg_addr) {
info->top_base = (void*) top_reg_addr;
ret = pinmux_set_host(info, id, pinmux);
if (id <= PINMUX_FUNC_ID_LCD2) {
id_restore = id;
pinmux_restore = pinmux;
}
} else {
pr_err("invalid pinmux address\n");
ret = -ENOMEM;
}
kfree(info);
return ret;
}
EXPORT_SYMBOL(pinmux_set_config);
int nvt_pinmux_probe(struct platform_device *pdev)
{
struct nvt_pinctrl_info *info;
struct nvt_gpio_info *gpio;
struct resource *nvt_mem_base;
struct device_node *top_np;
u32 value;
u32 pad_config[4] = {0, 0, 0, 0};
u32 gpio_config[2] = {0, 0};
int nr_pinmux = 0, nr_pad = 0, nr_gpio = 0;
info = devm_kzalloc(&pdev->dev, sizeof(struct nvt_pinctrl_info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "nvt pinmux mem alloc fail\n");
return -ENOMEM;
}
for_each_child_of_node(pdev->dev.of_node, top_np) {
if (!of_get_property(top_np, "gpio_config", NULL))
continue;
nr_gpio++;
}
gpio = devm_kzalloc(&pdev->dev, nr_gpio * sizeof(struct nvt_gpio_info), GFP_KERNEL);
if (!gpio) {
dev_err(&pdev->dev, "nvt gpio mem alloc fail\n");
return -ENOMEM;
}
nr_gpio = 0;
nvt_mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->top_base = devm_ioremap_resource(&pdev->dev, nvt_mem_base);
if (IS_ERR(info->top_base)) {
dev_err(&pdev->dev, "fail to get pinmux mem base\n");
return -ENOMEM;
}
top_reg_addr = (u32) info->top_base;
nvt_mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
info->pad_base = devm_ioremap_resource(&pdev->dev, nvt_mem_base);
if (IS_ERR(info->pad_base)) {
dev_err(&pdev->dev, "fail to get pad mem base\n");
return -ENOMEM;
}
nvt_mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
info->gpio_base = devm_ioremap_resource(&pdev->dev, nvt_mem_base);
if (IS_ERR(info->gpio_base)) {
dev_err(&pdev->dev, "fail to get gpio mem base\n");
return -ENOMEM;
}
for_each_child_of_node(pdev->dev.of_node, top_np) {
if (!of_property_read_u32(top_np, "pinmux", &value)) {
info->top_pinmux[nr_pinmux].pin_function = nr_pinmux;
info->top_pinmux[nr_pinmux].config = value;
nr_pinmux++;
}
if (!of_property_read_u32_array(top_np, "pad_config", pad_config, 4)) {
info->pad[nr_pad].pad_ds_pin = pad_config[0];
info->pad[nr_pad].driving = pad_config[1];
info->pad[nr_pad].pad_gpio_pin = pad_config[2];
info->pad[nr_pad].direction = pad_config[3];
nr_pad++;
}
if (!of_property_read_u32_array(top_np, "gpio_config", gpio_config, 2)) {
gpio[nr_gpio].gpio_pin = gpio_config[0];
gpio[nr_gpio].direction = gpio_config[1];
nr_gpio++;
}
}
if (nr_pinmux == 0)
return -ENOMEM;
pinmux_preset(info);
pad_preset(info);
if (pinmux_init(info))
nvt_pinmux_show_conflict(info);
if (nr_pad)
pad_init(info, nr_pad);
if (nr_gpio)
gpio_init(gpio, nr_gpio, info);
if(nvt_pinmux_proc_init())
return -ENOMEM;
return 0;
}
int nvt_pinctrl_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id nvt_pinctrl_of_match[] = {
{ .compatible = "nvt,nvt_top", },
{ },
};
static struct platform_driver nvt_pinctrl_driver = {
.driver = {
.name = "nvt-pinctrl",
.of_match_table = nvt_pinctrl_of_match,
},
.probe = nvt_pinmux_probe,
.remove = nvt_pinctrl_remove,
};
static int __init nvt_pinctrl_init(void)
{
return platform_driver_register(&nvt_pinctrl_driver);
}
arch_initcall(nvt_pinctrl_init);
static void __exit nvt_pinctrl_exit(void)
{
platform_driver_unregister(&nvt_pinctrl_driver);
}
module_exit(nvt_pinctrl_exit);
MODULE_DESCRIPTION("Novatek pinctrl driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.03.031");
MODULE_DEVICE_TABLE(of, nvt_pinctrl_of_match);