OpenCloudOS-Kernel/drivers/spi/spi-phytium-plat.c

199 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Phytium SPI core controller platform driver.
*
* Copyright (c) 2019-2021, Phytium Corporation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include "spi-phytium.h"
#define DRIVER_NAME "phytium_spi"
static int phytium_spi_probe(struct platform_device *pdev)
{
struct phytium_spi *fts;
struct resource *mem;
int ret;
int num_cs;
int cs_gpio;
int global_cs;
int i;
fts = devm_kzalloc(&pdev->dev, sizeof(struct phytium_spi),
GFP_KERNEL);
if (!fts)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
return -EINVAL;
}
fts->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(fts->regs)) {
dev_err(&pdev->dev, "SPI region map failed\n");
return PTR_ERR(fts->regs);
}
fts->irq = platform_get_irq(pdev, 0);
if (fts->irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
return fts->irq; /* -ENXIO */
}
if (pdev->dev.of_node) {
fts->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(fts->clk))
return PTR_ERR(fts->clk);
ret = clk_prepare_enable(fts->clk);
if (ret)
return ret;
fts->max_freq = clk_get_rate(fts->clk);
} else if (has_acpi_companion(&pdev->dev)) {
fts->max_freq = 48000000;
}
fts->bus_num = pdev->id;
device_property_read_u32(&pdev->dev, "reg-io-width", &fts->reg_io_width);
num_cs = 4;
device_property_read_u32(&pdev->dev, "num-cs", &num_cs);
fts->num_cs = num_cs;
if (pdev->dev.of_node) {
int i;
for (i = 0; i < fts->num_cs; i++) {
cs_gpio = of_get_named_gpio(pdev->dev.of_node,
"cs-gpios", i);
if (cs_gpio == -EPROBE_DEFER) {
ret = cs_gpio;
goto out;
}
if (gpio_is_valid(cs_gpio)) {
ret = devm_gpio_request(&pdev->dev, cs_gpio,
dev_name(&pdev->dev));
if (ret)
goto out;
}
}
} else if(has_acpi_companion(&pdev->dev)) {
int n;
int *cs;
struct gpio_desc *gpiod;
n = gpiod_count(&pdev->dev, "cs");
cs = devm_kcalloc(&pdev->dev, n, sizeof(int), GFP_KERNEL);
fts->cs = cs;
for (i = 0; i < n; i++) {
gpiod = devm_gpiod_get_index_optional(&pdev->dev, "cs", i,
GPIOD_OUT_LOW);
if (IS_ERR(gpiod)) {
ret = PTR_ERR(gpiod);
goto out;
}
cs_gpio = desc_to_gpio(gpiod);
cs[i] = cs_gpio;
}
}
device_property_read_u32(&pdev->dev, "global-cs", &global_cs);
fts->global_cs = global_cs;
ret = phytium_spi_add_host(&pdev->dev, fts);
if (ret)
goto out;
platform_set_drvdata(pdev, fts);
return 0;
out:
clk_disable_unprepare(fts->clk);
return ret;
}
static int phytium_spi_remove(struct platform_device *pdev)
{
struct phytium_spi *fts = platform_get_drvdata(pdev);
phytium_spi_remove_host(fts);
clk_disable_unprepare(fts->clk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int spi_suspend(struct device *dev)
{
struct phytium_spi *fts = dev_get_drvdata(dev);
return phytium_spi_suspend_host(fts);
}
static int spi_resume(struct device *dev)
{
struct phytium_spi *fts = dev_get_drvdata(dev);
return phytium_spi_resume_host(fts);
}
#endif
static SIMPLE_DEV_PM_OPS(phytium_spi_pm_ops, spi_suspend, spi_resume);
static const struct of_device_id phytium_spi_of_match[] = {
{ .compatible = "phytium,spi", .data = (void *)0 },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, phytium_spi_of_match);
static const struct acpi_device_id phytium_spi_acpi_match[] = {
{"PHYT000E", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, phytium_spi_acpi_match);
static struct platform_driver phytium_spi_driver = {
.probe = phytium_spi_probe,
.remove = phytium_spi_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(phytium_spi_of_match),
.acpi_match_table = ACPI_PTR(phytium_spi_acpi_match),
.pm = &phytium_spi_pm_ops,
},
};
module_platform_driver(phytium_spi_driver);
MODULE_AUTHOR("Yiqun Zhang <zhangyiqun@phytium.com.cn>");
MODULE_DESCRIPTION("Platform Driver for Phytium SPI controller core");
MODULE_LICENSE("GPL v2");