linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Add SUNIX SDC series mfd driver
@ 2021-05-14 11:27 Moriis Ku
  2021-05-14 14:08 ` Lee Jones
  0 siblings, 1 reply; 2+ messages in thread
From: Moriis Ku @ 2021-05-14 11:27 UTC (permalink / raw)
  To: lee.jones; +Cc: linux-kernel, Morris Ku

From: Morris Ku <saumah@gmail.com>

Signed-off-by: Morris Ku <saumah@gmail.com>
---
 sdc_mfd.c | 691 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sdc_mfd.h |  58 +++++
 sdc_pci.c | 100 ++++++++
 3 files changed, 849 insertions(+)
 create mode 100644 sdc_mfd.c
 create mode 100644 sdc_mfd.h
 create mode 100644 sdc_pci.c

diff --git a/sdc_mfd.c b/sdc_mfd.c
new file mode 100644
index 0000000..dc30f3b
--- /dev/null
+++ b/sdc_mfd.c
@@ -0,0 +1,691 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SUNIX SDC mfd driver.
+ *
+ * Copyright (C) 2021, SUNIX Co., Ltd.
+ *
+ * Authors: Jason Lee <jason_lee@sunix.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/ioport.h>
+#include <linux/property.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/idr.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/core.h>
+#include "sdc_mfd.h"
+
+struct cib_config {
+	unsigned int mem_addr_offset;
+	unsigned int mem_space_size;
+	unsigned char ic_brand;
+	unsigned char ic_model;
+};
+
+struct cib_uart {
+	unsigned int io_addr_offset;
+	unsigned char io_space_size;
+	unsigned int mem_addr_offset;
+	unsigned int mem_space_size;
+	unsigned short tx_fifo_size;
+	unsigned short rx_fifo_size;
+	unsigned int significand_of_ref_clk;
+	unsigned char exponent_of_ref_clk;
+	unsigned char rs232_cap;
+	unsigned char rs422_cap;
+	unsigned char rs485_cap;
+	unsigned char ahdc_cap;
+	unsigned char cs_cap;
+	unsigned char auto_rs422485_cap;
+	unsigned char rs422_termination_cap;
+	unsigned char rs485_termination_cap;
+	unsigned char ri_power_io_5v_cap;
+	unsigned char ri_power_io_12v_cap;
+	unsigned char dcd_power_io_5v_cap;
+	unsigned char dcd_power_io_12v_cap;
+};
+
+struct cib_dio_bank {
+	unsigned char number_of_io;
+	unsigned char bank_support_input_cap;
+	unsigned char bank_support_output_cap;
+	unsigned char rising_edge_trigger_cap;
+	unsigned char falling_edge_trigger_cap;
+};
+
+struct cib_dio {
+	unsigned int mem_addr_offset;
+	unsigned int mem_space_size;
+	unsigned char number_of_bank;
+	unsigned char share_same_direction_ctrl_cap;
+	unsigned char writing_settings_to_flash_cap;
+
+	struct cib_dio_bank bank[16];
+};
+
+struct cib_spi_device {
+	unsigned char type;
+	unsigned char number_of_gpio_input;
+	unsigned char number_of_gpio_output;
+	char name[10];
+	struct debugfs_blob_wrapper debugfs_blob_device_name;
+};
+
+struct cib_spi {
+	unsigned int mem_addr_offset;
+	unsigned int mem_space_size;
+	unsigned int significand_of_ref_clk;
+	unsigned char exponent_of_ref_clk;
+	unsigned char number_of_device;
+
+	struct cib_spi_device device[16];
+};
+
+struct cib_info {
+	unsigned char number;
+	unsigned char type;
+	unsigned char version;
+	unsigned char total_length_DW;
+	unsigned char resource_cap;
+	unsigned char event_header_type;
+
+	struct cib_config *config;
+	struct cib_uart *uart;
+	struct cib_dio *dio;
+	struct cib_spi *spi;
+};
+
+struct sdc_channel {
+	struct cib_info info;
+
+	struct property_entry *property;
+	struct resource *resource;
+	struct mfd_cell *cell;
+};
+
+struct sdc_mfd {
+	struct sdc_platform_info info;
+
+	void __iomem *bar0_membase;
+	unsigned long bar0_length;
+	unsigned long bar1_iobase;
+	unsigned long bar1_length;
+	void __iomem *bar2_membase;
+	unsigned long bar2_length;
+
+	unsigned char major_version;
+	unsigned char minor_version;
+	unsigned char available_chls;
+	unsigned char total_length_DW;
+	char model_name[18];
+
+	struct sdc_channel *channels;
+
+	struct device *dev;
+	int devid;
+
+	struct dentry *debugfs;
+	struct debugfs_blob_wrapper debugfs_blob_model_name;
+};
+
+static DEFINE_IDA(sdc_devid_ida);
+static struct dentry *sdc_mfd_debugfs;
+static int sdc_serial_id = 1;
+//static int sdc_gpio_id = 1;
+//static int sdc_spi_id = 1;
+
+static void sdc_get_config_info(struct cib_info *info, void __iomem *membase,
+					unsigned short ptr)
+{
+	unsigned int temp;
+	unsigned int offsetDW = 0;
+
+	if (!info || !info->config)
+		return;
+
+	offsetDW = 2;
+	info->config->mem_addr_offset = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 3;
+	info->config->mem_space_size = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 4;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->config->ic_brand = (unsigned char)((temp & 0x0000ff00) >> 8);
+	info->config->ic_model = (unsigned char)((temp & 0x00ff0000) >> 16);
+}
+
+static void sdc_get_uart_info(struct cib_info *info, void __iomem *membase,
+					unsigned short ptr)
+{
+	unsigned int temp;
+	unsigned int offsetDW = 0;
+
+	if (!info || !info->uart)
+		return;
+
+	offsetDW = 2;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->uart->io_addr_offset = (unsigned int)((temp & 0x00ffffff));
+	info->uart->io_space_size = (unsigned char)((temp & 0xff000000) >> 24);	
+	offsetDW = 3;
+	info->uart->mem_addr_offset = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 4;
+	info->uart->mem_space_size = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 5;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->uart->tx_fifo_size = (unsigned short)((temp & 0x0000ffff));
+	info->uart->rx_fifo_size = (unsigned short)((temp & 0xff0000) >> 16);
+	offsetDW = 6;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->uart->significand_of_ref_clk = temp & 0x00ffffff;
+	info->uart->exponent_of_ref_clk = (unsigned char)((temp & 0xff000000) >> 24);
+	offsetDW = 7;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->uart->rs232_cap             = (temp & 0x00000001) ? 0x01 : 0x00;
+	info->uart->rs422_cap             = (temp & 0x00000002) ? 0x01 : 0x00;
+	info->uart->rs485_cap             = (temp & 0x00000004) ? 0x01 : 0x00;
+	info->uart->ahdc_cap              = (temp & 0x00000008) ? 0x01 : 0x00;
+	info->uart->cs_cap                = (temp & 0x00000010) ? 0x01 : 0x00;
+	info->uart->auto_rs422485_cap     = (temp & 0x00000020) ? 0x01 : 0x00;
+	info->uart->rs422_termination_cap = (temp & 0x00000040) ? 0x01 : 0x00;
+	info->uart->rs485_termination_cap = (temp & 0x00000080) ? 0x01 : 0x00;
+	info->uart->ri_power_io_5v_cap    = (temp & 0x00000100) ? 0x01 : 0x00;
+	info->uart->ri_power_io_12v_cap   = (temp & 0x00000200) ? 0x01 : 0x00;
+	info->uart->dcd_power_io_5v_cap   = (temp & 0x00000400) ? 0x01 : 0x00;
+	info->uart->dcd_power_io_12v_cap  = (temp & 0x00000800) ? 0x01 : 0x00;
+}
+
+static void sdc_get_dio_info(struct cib_info *info, void __iomem *membase,
+					unsigned short ptr)
+{
+	unsigned int temp;
+	unsigned int offsetDW = 0;
+	unsigned char i;
+
+	if (!info || !info->dio)
+		return;
+
+	offsetDW = 2;
+	info->dio->mem_addr_offset = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 3;
+	info->dio->mem_space_size = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 4;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->dio->number_of_bank = (unsigned char)((temp & 0x000000ff));
+	info->dio->share_same_direction_ctrl_cap = (temp & 0x00000100) ? 0x01 : 0x00;
+	info->dio->writing_settings_to_flash_cap = (temp & 0x00000200) ? 0x01 : 0x00;
+
+	for (i = 0; i < info->dio->number_of_bank; i++) {
+		offsetDW++;
+		temp = readl(membase + ptr + (offsetDW * 4));
+		info->dio->bank[i].number_of_io = (unsigned char)((temp & 0x000000ff));
+		info->dio->bank[i].bank_support_input_cap = (temp & 0x00000100) ? 0x01 : 0x00;
+		info->dio->bank[i].bank_support_output_cap = (temp & 0x00000200) ? 0x01 : 0x00;
+		info->dio->bank[i].rising_edge_trigger_cap = (temp & 0x00000400) ? 0x01 : 0x00;
+		info->dio->bank[i].falling_edge_trigger_cap = (temp & 0x00000800) ? 0x01 : 0x00;
+	}
+}
+
+static void sdc_get_spi_info(struct cib_info *info, void __iomem *membase,
+					unsigned short ptr)
+{
+	unsigned int temp;
+	unsigned int offsetDW = 0;
+	unsigned char i;
+
+	if (!info || !info->spi)
+		return;
+
+	offsetDW = 2;
+	info->spi->mem_addr_offset = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 3;
+	info->spi->mem_space_size = readl(membase + ptr + (offsetDW * 4));
+	offsetDW = 4;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->spi->significand_of_ref_clk = temp & 0x00ffffff;
+	info->spi->exponent_of_ref_clk = (unsigned char)((temp & 0xff000000) >> 24);
+	offsetDW = 5;
+	temp = readl(membase + ptr + (offsetDW * 4));
+	info->spi->number_of_device = (unsigned char)((temp & 0x000000ff));
+
+	for (i = 0; i < info->spi->number_of_device; i++) {
+		offsetDW++;
+		temp = readl(membase + ptr + (offsetDW * 4));
+		info->spi->device[i].type = (unsigned char)((temp & 0x000000ff));
+		info->spi->device[i].number_of_gpio_input = (unsigned char)((temp & 0x00000f00) >> 8);
+		info->spi->device[i].number_of_gpio_output = (unsigned char)((temp & 0x0000f000) >> 12);
+
+		offsetDW++;
+		temp = readl(membase + ptr + (offsetDW * 4));
+		info->spi->device[i].name[0] = (unsigned char)((temp & 0x000000ff));
+		info->spi->device[i].name[1] = (unsigned char)((temp & 0x0000ff00) >> 8);
+		info->spi->device[i].name[2] = (unsigned char)((temp & 0x00ff0000) >> 16);
+		info->spi->device[i].name[3] = (unsigned char)((temp & 0xff000000) >> 24);
+
+		offsetDW++;
+		temp = readl(membase + ptr + (offsetDW * 4));
+		info->spi->device[i].name[4] = (unsigned char)((temp & 0x000000ff));
+		info->spi->device[i].name[5] = (unsigned char)((temp & 0x0000ff00) >> 8);
+		info->spi->device[i].name[6] = (unsigned char)((temp & 0x00ff0000) >> 16);
+		info->spi->device[i].name[7] = (unsigned char)((temp & 0xff000000) >> 24);
+
+		info->spi->device[i].name[8] = 0x0a;
+	}
+}
+
+static int sdc_debugfs_add(struct sdc_mfd *sdc)
+{
+	struct dentry *root_dir;
+	struct dentry *chl_dir;
+	char chl_name[20];
+	struct dentry *bank_dir;
+	char bank_name[20];
+	struct dentry *device_dir;
+	char device_name[20];
+	struct sdc_channel *chl = NULL;
+	int i;
+	int j;
+
+	root_dir = debugfs_create_dir(dev_name(sdc->dev), sdc_mfd_debugfs);
+	if (IS_ERR(root_dir))
+		return PTR_ERR(root_dir);
+
+	debugfs_create_u32("devid", 0644, root_dir, &sdc->devid);
+	debugfs_create_u32("bus_number", 0644, root_dir, &sdc->info.bus_number);
+	debugfs_create_u32("device_number", 0644, root_dir, &sdc->info.device_number);
+	debugfs_create_u32("irq", 0644, root_dir, &sdc->info.irq);
+	debugfs_create_u8("major_version", 0644, root_dir, &sdc->major_version);
+	debugfs_create_u8("minor_version", 0644, root_dir, &sdc->minor_version);
+	debugfs_create_u8("available_chls", 0644, root_dir, &sdc->available_chls);
+	sdc->debugfs_blob_model_name.data = sdc->model_name;
+	sdc->debugfs_blob_model_name.size = strlen(sdc->model_name) + 1;
+	debugfs_create_blob("model_name", 0644, root_dir, &sdc->debugfs_blob_model_name);
+
+	for (i = 0; i < sdc->available_chls; i++) {
+		chl = &sdc->channels[i];
+		if (chl) {
+			memset(chl_name, 0, sizeof(char) * 20);
+			sprintf(chl_name, "chl%d", i);
+			chl_dir = debugfs_create_dir(chl_name, root_dir);
+
+			if (chl_dir) {
+				debugfs_create_x8("number", 0644, chl_dir, &chl->info.number);
+				debugfs_create_x8("type", 0644, chl_dir, &chl->info.type);
+				debugfs_create_x8("version", 0644, chl_dir, &chl->info.version);
+				debugfs_create_x8("total_length_DW", 0644, chl_dir, &chl->info.total_length_DW);
+				debugfs_create_x8("resource_cap", 0644, chl_dir, &chl->info.resource_cap);
+				debugfs_create_x8("event_header_type", 0644, chl_dir, &chl->info.event_header_type);
+
+				switch (chl->info.type) {
+					case 0x00:
+						if (chl->info.config) {
+							debugfs_create_x32("mem_addr_offset", 0644, chl_dir, &chl->info.config->mem_addr_offset);
+							debugfs_create_x32("mem_space_size", 0644, chl_dir, &chl->info.config->mem_space_size);
+							debugfs_create_x8("ic_brand", 0644, chl_dir, &chl->info.config->ic_brand);
+							debugfs_create_x8("ic_model", 0644, chl_dir, &chl->info.config->ic_model);
+						}
+						break;
+
+					case 0x01:
+						if (chl->info.uart) {
+							debugfs_create_x32("io_addr_offset", 0644, chl_dir, &chl->info.uart->io_addr_offset);
+							debugfs_create_x8("io_space_size", 0644, chl_dir, &chl->info.uart->io_space_size);
+							debugfs_create_x32("mem_addr_offset", 0644, chl_dir, &chl->info.uart->mem_addr_offset);
+							debugfs_create_x32("mem_space_size", 0644, chl_dir, &chl->info.uart->mem_space_size);
+							debugfs_create_x16("tx_fifo_size", 0644, chl_dir, &chl->info.uart->tx_fifo_size);
+							debugfs_create_x16("rx_fifo_size", 0644, chl_dir, &chl->info.uart->rx_fifo_size);
+							debugfs_create_x32("significand_of_ref_clk", 0644, chl_dir, &chl->info.uart->significand_of_ref_clk);
+							debugfs_create_x8("exponent_of_ref_clk", 0644, chl_dir, &chl->info.uart->exponent_of_ref_clk);
+							debugfs_create_x8("rs232_cap", 0644, chl_dir, &chl->info.uart->rs232_cap);
+							debugfs_create_x8("rs422_cap", 0644, chl_dir, &chl->info.uart->rs422_cap);
+							debugfs_create_x8("rs485_cap", 0644, chl_dir, &chl->info.uart->rs485_cap);
+							debugfs_create_x8("ahdc_cap", 0644, chl_dir, &chl->info.uart->ahdc_cap);
+							debugfs_create_x8("cs_cap", 0644, chl_dir, &chl->info.uart->cs_cap);
+							debugfs_create_x8("auto_rs422485_cap", 0644, chl_dir, &chl->info.uart->auto_rs422485_cap);
+							debugfs_create_x8("rs422_termination_cap", 0644, chl_dir, &chl->info.uart->rs422_termination_cap);
+							debugfs_create_x8("rs485_termination_cap", 0644, chl_dir, &chl->info.uart->rs485_termination_cap);
+							debugfs_create_x8("ri_power_io_5v_cap", 0644, chl_dir, &chl->info.uart->ri_power_io_5v_cap);
+							debugfs_create_x8("ri_power_io_12v_cap", 0644, chl_dir, &chl->info.uart->ri_power_io_12v_cap);
+							debugfs_create_x8("dcd_power_io_5v_cap", 0644, chl_dir, &chl->info.uart->dcd_power_io_5v_cap);
+							debugfs_create_x8("dcd_power_io_12v_cap", 0644, chl_dir, &chl->info.uart->dcd_power_io_12v_cap);
+						}
+						break;
+
+					case 0x02:
+						if (chl->info.dio) {
+							debugfs_create_x32("mem_addr_offset", 0644, chl_dir, &chl->info.dio->mem_addr_offset);
+							debugfs_create_x32("mem_space_size", 0644, chl_dir, &chl->info.dio->mem_space_size);
+							debugfs_create_x8("number_of_bank", 0644, chl_dir, &chl->info.dio->number_of_bank);
+							debugfs_create_x8("share_same_direction_ctrl_cap", 0644, chl_dir, &chl->info.dio->share_same_direction_ctrl_cap);
+							debugfs_create_x8("writing_settings_to_flash_cap", 0644, chl_dir, &chl->info.dio->writing_settings_to_flash_cap);
+
+							for (j = 0; j < chl->info.dio->number_of_bank; j++) {
+								memset(bank_name, 0, sizeof(char) * 20);
+								sprintf(bank_name, "bank%d", j);
+								bank_dir = debugfs_create_dir(bank_name, chl_dir);
+
+								if (bank_dir) {
+									debugfs_create_x8("number_of_io", 0644, bank_dir, &chl->info.dio->bank[j].number_of_io);
+									debugfs_create_x8("bank_support_input_cap", 0644, bank_dir, &chl->info.dio->bank[j].bank_support_input_cap);
+									debugfs_create_x8("bank_support_output_cap", 0644, bank_dir, &chl->info.dio->bank[j].bank_support_output_cap);
+									debugfs_create_x8("rising_edge_trigger_cap", 0644, bank_dir, &chl->info.dio->bank[j].rising_edge_trigger_cap);
+									debugfs_create_x8("falling_edge_trigger_cap", 0644, bank_dir, &chl->info.dio->bank[j].falling_edge_trigger_cap);
+								} else {
+									dev_warn(sdc->dev, "create channel (%d) bank (%d) debugfs fail\n", i, j);
+								}
+							}
+						}
+						break;
+
+					case 0x03:
+						if (chl->info.spi) {
+							debugfs_create_x32("mem_addr_offset", 0644, chl_dir, &chl->info.spi->mem_addr_offset);
+							debugfs_create_x32("mem_space_size", 0644, chl_dir, &chl->info.spi->mem_space_size);
+							debugfs_create_x32("significand_of_ref_clk", 0644, chl_dir, &chl->info.spi->significand_of_ref_clk);
+							debugfs_create_x8("exponent_of_ref_clk", 0644, chl_dir, &chl->info.spi->exponent_of_ref_clk);
+							debugfs_create_x8("number_of_device", 0644, chl_dir, &chl->info.spi->number_of_device);
+
+							for (j = 0; j < chl->info.spi->number_of_device; j++) {
+								memset(device_name, 0, sizeof(char) * 20);
+								sprintf(device_name, "device%d", j);
+								device_dir = debugfs_create_dir(device_name, chl_dir);
+
+								if (device_dir) {
+									debugfs_create_x8("type", 0644, device_dir, &chl->info.spi->device[j].type);
+									debugfs_create_x8("number_of_gpio_input", 0644, device_dir, &chl->info.spi->device[j].number_of_gpio_input);
+									debugfs_create_x8("number_of_gpio_output", 0644, device_dir, &chl->info.spi->device[j].number_of_gpio_output);
+									
+									chl->info.spi->device[j].debugfs_blob_device_name.data = chl->info.spi->device[j].name;
+									chl->info.spi->device[j].debugfs_blob_device_name.size = strlen(chl->info.spi->device[j].name) + 1;	
+									debugfs_create_blob("name", 0644, device_dir, &chl->info.spi->device[j].debugfs_blob_device_name);
+								} else {
+									dev_warn(sdc->dev, "create channel (%d) device (%d) debugfs fail\n", i, j);
+								}
+							}
+						}
+						break;
+				}
+			} else {
+				dev_warn(sdc->dev, "create channel (%d) debugfs fail\n", i);
+			}
+		} else {
+			dev_warn(sdc->dev, "channel (%d) pointer invalid\n", i);
+		}
+	}
+
+	sdc->debugfs = root_dir;
+	return 0;
+}
+
+static void sdc_debugfs_remove(struct sdc_mfd *sdc)
+{
+	debugfs_remove_recursive(sdc->debugfs);
+}
+
+int sdc_probe(struct device *dev, struct sdc_platform_info *info)
+{
+	int ret;
+	int i;
+	int j;
+	struct sdc_mfd *sdc = NULL;
+	unsigned int temp;
+	struct sdc_channel *chl = NULL;
+	unsigned short next_cib_ptr = 0;
+	unsigned short next_cib_ptr_backup = 0;
+
+	if (!info || !info->pdev || info->irq <= 0)
+		return -EINVAL;
+
+	sdc = devm_kzalloc(dev, sizeof(*sdc), GFP_KERNEL);
+	if (!sdc)
+		return -ENOMEM;
+
+	sdc->info.pdev = info->pdev;
+	sdc->info.bus_number = info->bus_number;
+	sdc->info.device_number = info->device_number;
+	sdc->info.irq = info->irq;
+
+	sdc->bar0_length = pci_resource_len(info->pdev, 0);
+	sdc->bar0_membase = devm_ioremap_uc(dev, pci_resource_start(info->pdev, 0), pci_resource_len(info->pdev, 0));
+	if (!sdc->bar0_membase)
+		return -ENOMEM;
+
+	sdc->bar1_length = pci_resource_len(info->pdev, 1);	
+	sdc->bar1_iobase = pci_resource_start(info->pdev, 1);
+
+	sdc->bar2_length = pci_resource_len(info->pdev, 2);
+	sdc->bar2_membase = devm_ioremap_uc(dev, pci_resource_start(info->pdev, 2), pci_resource_len(info->pdev, 2));
+	if (!sdc->bar2_membase)
+		return -ENOMEM;
+
+	temp = readl(sdc->bar2_membase);
+	sdc->major_version = (unsigned char)((temp & 0x000000ff));
+	sdc->minor_version = (unsigned char)((temp & 0x0000ff00) >> 8);
+	sdc->available_chls = (unsigned char)((temp & 0x00ff0000) >> 16);
+	sdc->total_length_DW = (unsigned char)((temp & 0xff000000) >> 24);
+
+	temp = readl(sdc->bar2_membase + 4);
+	next_cib_ptr = next_cib_ptr_backup = (unsigned short)((temp & 0x0000ffff));
+
+	j = 0;
+	for (i = 0; i < 4; i++) {
+		temp = readl(sdc->bar2_membase + 8 + (i * 4));
+		sdc->model_name[j++] = (char)((temp & 0x000000ff));
+		sdc->model_name[j++] = (char)((temp & 0x0000ff00) >> 8);
+		sdc->model_name[j++] = (char)((temp & 0x00ff0000) >> 16);
+		sdc->model_name[j++] = (char)((temp & 0xff000000) >> 24);
+	}
+	sdc->model_name[strlen(sdc->model_name)] = 0x0a;
+
+	sdc->channels = devm_kzalloc(dev, sizeof(struct sdc_channel) * sdc->available_chls, GFP_KERNEL);
+	if (!sdc->channels)
+		return -ENOMEM;
+
+	for (i = 0; i < sdc->available_chls; i++) {
+		chl = &sdc->channels[i];
+
+		next_cib_ptr_backup = next_cib_ptr;
+
+		temp = readl(sdc->bar2_membase + next_cib_ptr);
+		chl->info.number = (unsigned char)((temp & 0x000000ff));
+		chl->info.type = (unsigned char)((temp & 0x0000ff00) >> 8);
+		chl->info.version = (unsigned char)((temp & 0x00ff0000) >> 16);
+		chl->info.total_length_DW = (unsigned char)((temp & 0xff000000) >> 24);
+
+		temp = readl(sdc->bar2_membase + next_cib_ptr + 4);
+		next_cib_ptr = temp & 0x0000ffff;
+		chl->info.resource_cap = (unsigned char)((temp & 0x00ff0000) >> 16);
+		chl->info.event_header_type = (unsigned char)((temp & 0xff000000) >> 24);
+
+		switch (chl->info.type) {
+			case 0x00:
+				chl->info.config = devm_kzalloc(dev, sizeof(struct cib_config), GFP_KERNEL);
+				if (!chl->info.config)
+					goto err_alloc_channel;
+				sdc_get_config_info(&chl->info, sdc->bar2_membase, next_cib_ptr_backup);
+				break;
+
+			case 0x01:
+				chl->info.uart = devm_kzalloc(dev, sizeof(struct cib_uart), GFP_KERNEL);
+				if (!chl->info.uart) {
+					ret = -ENOMEM;
+					goto err_alloc_channel;
+				}
+				sdc_get_uart_info(&chl->info, sdc->bar2_membase, next_cib_ptr_backup);
+
+				chl->property = devm_kzalloc(dev, sizeof(struct property_entry) * 18, GFP_KERNEL);
+				if (!chl->property) {
+					ret = -ENOMEM;
+					goto err_alloc_channel;
+				}
+				chl->property[0] = PROPERTY_ENTRY_U32("bus_number", sdc->info.bus_number);
+				chl->property[1] = PROPERTY_ENTRY_U32("device_number", sdc->info.device_number);
+				chl->property[2] = PROPERTY_ENTRY_U32("irq", sdc->info.irq);
+				chl->property[3] = PROPERTY_ENTRY_U8("number", chl->info.number);
+				chl->property[4] = PROPERTY_ENTRY_U8("version", chl->info.version);
+				chl->property[5] = PROPERTY_ENTRY_U8("resource_cap", chl->info.resource_cap);
+				chl->property[6] = PROPERTY_ENTRY_U8("event_header_type", chl->info.event_header_type);
+				chl->property[7] = PROPERTY_ENTRY_U16("tx_fifo_size", chl->info.uart->tx_fifo_size);
+				chl->property[8] = PROPERTY_ENTRY_U16("rx_fifo_size", chl->info.uart->rx_fifo_size);
+				chl->property[9] = PROPERTY_ENTRY_U32("significand_of_ref_clk", chl->info.uart->significand_of_ref_clk);
+				chl->property[10] = PROPERTY_ENTRY_U8("exponent_of_ref_clk", chl->info.uart->exponent_of_ref_clk);
+				chl->property[11] = PROPERTY_ENTRY_U8("rs232_cap", chl->info.uart->rs232_cap);
+				chl->property[12] = PROPERTY_ENTRY_U8("rs422_cap", chl->info.uart->rs422_cap);
+				chl->property[13] = PROPERTY_ENTRY_U8("rs485_cap", chl->info.uart->rs485_cap);
+				chl->property[14] = PROPERTY_ENTRY_U8("ahdc_cap", chl->info.uart->ahdc_cap);
+				chl->property[15] = PROPERTY_ENTRY_U8("cs_cap", chl->info.uart->cs_cap);
+				chl->property[16] = PROPERTY_ENTRY_U8("rs422_termination_cap", chl->info.uart->rs422_termination_cap);
+				chl->property[17] = PROPERTY_ENTRY_U8("rs485_termination_cap", chl->info.uart->rs485_termination_cap);
+
+				chl->resource = devm_kzalloc(dev, sizeof(struct resource) * 2, GFP_KERNEL);
+				if (!chl->resource) {
+					ret = -ENOMEM;
+					goto err_alloc_channel;
+				}
+				chl->resource[0].start = sdc->bar1_iobase + chl->info.uart->io_addr_offset;
+				chl->resource[0].end = sdc->bar1_iobase + chl->info.uart->io_addr_offset + chl->info.uart->io_space_size - 1;
+				chl->resource[0].name = "iobase";
+				chl->resource[0].flags = IORESOURCE_IO;
+				chl->resource[0].desc = IORES_DESC_NONE;
+
+				chl->resource[1].start = 0;
+				chl->resource[1].end =  0;
+				chl->resource[1].name = "irq";
+				chl->resource[1].flags = IORESOURCE_IRQ;
+				chl->resource[1].desc = IORES_DESC_NONE;
+
+				chl->cell = devm_kzalloc(dev, sizeof(struct mfd_cell), GFP_KERNEL);
+				if (!chl->cell) {
+					ret = -ENOMEM;
+					goto err_alloc_channel;
+				}
+				chl->cell->name = "sdc_serial";
+				chl->cell->id = sdc_serial_id++;
+				chl->cell->properties = chl->property;
+				chl->cell->num_resources = 2;
+				chl->cell->resources = chl->resource;
+				break;
+
+			case 0x02:
+				chl->info.dio = devm_kzalloc(dev, sizeof(struct cib_dio), GFP_KERNEL);
+				if (!chl->info.dio) {
+					ret = -ENOMEM;
+					goto err_alloc_channel;
+				}
+				sdc_get_dio_info(&chl->info, sdc->bar2_membase, next_cib_ptr_backup);
+				break;
+
+			case 0x03:
+				chl->info.spi = devm_kzalloc(dev, sizeof(struct cib_spi), GFP_KERNEL);
+				if (!chl->info.spi) {
+					ret = -ENOMEM;
+					goto err_alloc_channel;
+				}
+				sdc_get_spi_info(&chl->info, sdc->bar2_membase, next_cib_ptr_backup);
+				break;
+		}
+	}
+
+	sdc->dev = dev;
+	dev_set_drvdata(dev, sdc);
+
+	ret = ida_simple_get(&sdc_devid_ida, 0, 0, GFP_KERNEL);
+	if (ret < 0)
+		goto err_ida_simple_get;
+	sdc->devid = ret;
+
+	ret = sdc_debugfs_add(sdc);
+	if (ret)
+		dev_warn(dev, "failed to create debugfs entries\n");
+
+	for (i = 0; i < sdc->available_chls; i++) {
+		chl = &sdc->channels[i];
+
+		if (chl->cell) {
+			ret = mfd_add_devices(dev, sdc->devid, chl->cell, 1, NULL, sdc->info.irq, NULL);
+			if (ret) 
+				goto err_mfd_add_device;
+		}
+	}
+
+	dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND);
+	return 0;
+
+err_mfd_add_device:
+	sdc_debugfs_remove(sdc);
+
+err_ida_simple_get:
+
+err_alloc_channel:
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sdc_probe);
+
+void sdc_remove(struct device *dev)
+{
+	struct sdc_mfd *sdc = dev_get_drvdata(dev);
+
+	mfd_remove_devices(dev);
+	sdc_debugfs_remove(sdc);
+	ida_simple_remove(&sdc_devid_ida, sdc->devid);
+}
+EXPORT_SYMBOL_GPL(sdc_remove);
+
+static int resume_sdc_device(struct device *dev, void *data)
+{
+	if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
+		pm_runtime_resume(dev);
+
+	return 0;
+}
+
+int sdc_prepare(struct device *dev)
+{
+	device_for_each_child_reverse(dev, NULL, resume_sdc_device);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdc_prepare);
+
+int sdc_suspend(struct device *dev)
+{
+	// save context
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdc_suspend);
+
+int sdc_resume(struct device *dev)
+{
+	// restore context
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdc_resume);
+
+static int __init sdc_init(void)
+{
+	sdc_mfd_debugfs = debugfs_create_dir("sdc_mfd", NULL);
+	return 0;
+}
+module_init(sdc_init);
+
+static void __exit sdc_exit(void)
+{
+	ida_destroy(&sdc_devid_ida);
+	debugfs_remove(sdc_mfd_debugfs);
+}
+module_exit(sdc_exit);
+
+MODULE_AUTHOR("Jason Lee <jason_lee@sunix.com>");
+MODULE_DESCRIPTION("SUNIX SDC mfd driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0.0.0");
+
+MODULE_SOFTDEP("pre: 8250_sdc");
+
+
diff --git a/sdc_mfd.h b/sdc_mfd.h
new file mode 100644
index 0000000..7bfe490
--- /dev/null
+++ b/sdc_mfd.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SUNIX SDC mfd driver.
+ *
+ * Copyright (C) 2021, SUNIX Co., Ltd.
+ *
+ * Authors: Jason Lee <jason_lee@sunix.com>
+ */
+
+#ifndef __SDC_MFD_H
+#define __SDC_MFD_H
+
+#include <linux/pm.h>
+
+struct device;
+struct resource;
+struct property_entry;
+
+struct sdc_platform_info {
+	struct pci_dev *pdev;
+	int bus_number;
+	int device_number;
+	int irq;
+};
+
+int sdc_probe(struct device *dev, struct sdc_platform_info *info);
+void sdc_remove(struct device *dev);
+
+#ifdef CONFIG_PM
+int sdc_prepare(struct device *dev);
+int sdc_suspend(struct device *dev);
+int sdc_resume(struct device *dev);
+
+#ifdef CONFIG_PM_SLEEP
+#define SDC_SLEEP_PM_OPS				\
+	.prepare = sdc_prepare,				\
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(sdc_suspend, sdc_resume)
+#else
+#define SDC_SLEEP_PM_OPS
+#endif // CONFIG_PM_SLEEP
+
+#define SDC_RUNTIME_PM_OPS				\
+	.runtime_suspend = sdc_suspend,		\
+	.runtime_resume = sdc_resume,
+#else // !CONFIG_PM
+#define SDC_SLEEP_PM_OPS
+#define SDC_RUNTIME_PM_OPS
+#endif // CONFIG_PM
+
+#define SDC_PM_OPS(name)				\
+const struct dev_pm_ops name = 	{		\
+	SDC_SLEEP_PM_OPS					\
+	SDC_RUNTIME_PM_OPS					\
+}
+
+#endif // __SDC_MFD_H
+
+
diff --git a/sdc_pci.c b/sdc_pci.c
new file mode 100644
index 0000000..085a5ab
--- /dev/null
+++ b/sdc_pci.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SUNIX SDC PCIe driver.
+ *
+ * Copyright (C) 2021, SUNIX Co., Ltd.
+ *
+ * Authors: Jason Lee <jason_lee@sunix.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/ioport.h>
+#include <linux/property.h>
+#include <linux/pci.h>
+#include "sdc_mfd.h"
+
+static int sdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int ret;
+	struct sdc_platform_info info;
+	unsigned long flags;
+
+	ret = pcim_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	flags = pci_resource_flags(pdev, 0);
+	if (!(flags & IORESOURCE_MEM)) {
+		pr_err("bar0 resource flag (x%lx) invalid\n", flags);
+		return -ENODEV;
+	}
+
+	flags = pci_resource_flags(pdev, 1);
+	if (!(flags & IORESOURCE_IO)) {
+		pr_err("bar1 resource flag (x%lx) invalid\n", flags);
+		return -ENODEV;
+	}
+
+	flags = pci_resource_flags(pdev, 2);
+	if (!(flags & IORESOURCE_MEM)) {
+		pr_err("bar2 resource flag (x%lx) invalid\n", flags);
+		return -ENODEV;
+	}
+
+	memset(&info, 0, sizeof(info));
+	info.pdev = pdev;
+	info.bus_number = pdev->bus->number;
+	info.device_number = PCI_SLOT(pdev->devfn);
+	info.irq = pdev->irq;
+
+	ret = sdc_probe(&pdev->dev, &info);
+	if (ret)
+		goto err_probe;
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_allow(&pdev->dev);
+
+err_probe:
+
+	return ret;
+}
+
+static void sdc_pci_remove(struct pci_dev *pdev)
+{
+	pm_runtime_forbid(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	sdc_remove(&pdev->dev);
+}
+
+static SDC_PM_OPS(sdc_pci_pm_ops);
+
+static const struct pci_device_id sdc_pci_ids[] = {
+	{ PCI_VDEVICE(SUNIX, 0x2000) },
+	{ },
+};
+MODULE_DEVICE_TABLE(pci, sdc_pci_ids);
+
+static struct pci_driver sdc_pci_driver = {
+	.name = "sdc_pci",
+	.id_table = sdc_pci_ids,
+	.probe = sdc_pci_probe,
+	.remove = sdc_pci_remove,
+	.driver = {
+		.pm = &sdc_pci_pm_ops,
+	},
+};
+module_pci_driver(sdc_pci_driver);
+
+MODULE_AUTHOR("Jason Lee <jason_lee@sunix.com>");
+MODULE_DESCRIPTION("SUNIX SDC PCIe driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0.0.0");
+
+MODULE_SOFTDEP("pre: sdc_mfd");
+
+
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] Add SUNIX SDC series mfd driver
  2021-05-14 11:27 [PATCH] Add SUNIX SDC series mfd driver Moriis Ku
@ 2021-05-14 14:08 ` Lee Jones
  0 siblings, 0 replies; 2+ messages in thread
From: Lee Jones @ 2021-05-14 14:08 UTC (permalink / raw)
  To: Moriis Ku; +Cc: linux-kernel

On Fri, 14 May 2021, Moriis Ku wrote:

> From: Morris Ku <saumah@gmail.com>
> 
> Signed-off-by: Morris Ku <saumah@gmail.com>
> ---
>  sdc_mfd.c | 691 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  sdc_mfd.h |  58 +++++
>  sdc_pci.c | 100 ++++++++
>  3 files changed, 849 insertions(+)
>  create mode 100644 sdc_mfd.c
>  create mode 100644 sdc_mfd.h
>  create mode 100644 sdc_pci.c

Please read and adhere to the upstreaming documentation [0] before
submitting any more patches.  There are lots of basic issues with this
patch.

If after reading, you still don't know what I'm referring to, let me
know and I'll do a very quick review just to point you in the right
direction.

Must better if you can use and digest the docs though, it will serve
you well in the future.

[0] Start here: Documentation/process/

> diff --git a/sdc_mfd.c b/sdc_mfd.c
> new file mode 100644
> index 0000000..dc30f3b
> --- /dev/null
> +++ b/sdc_mfd.c
> @@ -0,0 +1,691 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SUNIX SDC mfd driver.
> + *
> + * Copyright (C) 2021, SUNIX Co., Ltd.
> + *
> + * Authors: Jason Lee <jason_lee@sunix.com>
> + */

-- 
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-05-14 14:09 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-14 11:27 [PATCH] Add SUNIX SDC series mfd driver Moriis Ku
2021-05-14 14:08 ` Lee Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).