From c43464f01f3a38dea0dfdd3f55dd46ab067f8cdb Mon Sep 17 00:00:00 2001 From: wangzc Date: Mon, 19 Dec 2022 11:13:14 +0800 Subject: [PATCH 01/41] misc: Add phytium-lpc-snoop dirve for temporary use Signed-off-by: wangzc --- arch/arm64/boot/dts/phytium/pe220x.dtsi | 6 + drivers/misc/Kconfig | 8 + drivers/misc/Makefile | 1 + drivers/misc/phytium-lpc-snoop.c | 322 ++++++++++++++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 drivers/misc/phytium-lpc-snoop.c diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index 3f9df3b19c..dd104afeb2 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -312,6 +312,12 @@ bt: bt@48 { interrupts = ; status = "disabled"; }; + lpc_snoop: lpc-snoop@150 { + compatible = "phytium,pe2201-lpc-snoop"; + reg = <0x150 0x10>; + interrupts = ; + status = "disabled"; + }; }; /* MIO */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3726eacdf6..1f401d5e06 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -501,6 +501,14 @@ config ASPEED_LPC_SNOOP allows the BMC to listen on and save the data written by the host to an arbitrary LPC I/O port. +config PHYTIUM_LPC_SNOOP + tristate "Phytium PE2201 HOST LPC snoop support" + depends on ARCH_PHYTIUM && REGMAP && MFD_SYSCON + help + Provides a driver to control the LPC snoop interface which + allows the BMC to listen on and save the data written by + the host to an arbitrary LPC I/O port. + config PCI_ENDPOINT_TEST depends on PCI select CRC32 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af22bbc3d0..9b5daeae77 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o +obj-$(CONFIG_PHYTIUM_LPC_SNOOP) += phytium-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ diff --git a/drivers/misc/phytium-lpc-snoop.c b/drivers/misc/phytium-lpc-snoop.c new file mode 100644 index 0000000000..491e73375d --- /dev/null +++ b/drivers/misc/phytium-lpc-snoop.c @@ -0,0 +1,322 @@ +/* + * Copyright 2020-2021 Phytium Technology Co.,Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Provides a simple driver to control the PHYTIUM PE2201 LPC snoop interface which + * allows the BMC to listen on and save the data written by + * the host to an arbitrary LPC I/O port. + * + * Typically used by the BMC to "watch" host boot progress via port + * 0x80 writes made by the BIOS during the boot process. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "phytium-lpc-snoop" + +#define NUM_SNOOP_CHANNELS 2 +#define SNOOP_FIFO_SIZE 2048 + +#define snp_enable_reg 0x150 +#define snp_enable_reg_snp1_en BIT(0) +#define snp_enable_reg_snp1_int_en BIT(1) +#define snp_enable_reg_snp2_en BIT(2) +#define snp_enable_reg_snp2_int_en BIT(3) + +#define snp_status_reg 0x154 +#define snp_status_reg_snp1_int BIT(0) +#define snp_status_reg_snp2_int BIT(1) + +#define snp_addr_reg 0x158 +#define snp_addr_reg_snp1_addr GENMASK(15, 0) +#define snp_addr_reg_snp1_shift 0 +#define snp_addr_reg_snp2_addr GENMASK(31, 16) +#define snp_addr_reg_snp2_shift 16 + +#define snp_data_reg 0x15c +#define snp_data_reg_snp1_data_reg GENMASK(7, 0) +#define snp_data_reg_snp1_shift 0 +#define snp_data_reg_snp2_data_reg GENMASK(15, 8) +#define snp_data_reg_snp2_shift 8 + +struct phytium_lpc_snoop_channel { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +struct phytium_lpc_snoop { + struct regmap *regmap; + int irq; + struct phytium_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; +}; + +static struct phytium_lpc_snoop_channel *snoop_file_to_chan(struct file *file) +{ + return container_of(file->private_data, + struct phytium_lpc_snoop_channel, + miscdev); +} + +static ssize_t snoop_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&chan->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(chan->wq, + !kfifo_is_empty(&chan->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static unsigned int snoop_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); + + poll_wait(file, &chan->wq, pt); + return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; +} + +static const struct file_operations snoop_fops = { + .owner = THIS_MODULE, + .read = snoop_file_read, + .poll = snoop_file_poll, + .llseek = noop_llseek, +}; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +static void put_fifo_with_discard(struct phytium_lpc_snoop_channel *chan, u8 val) +{ + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + wake_up_interruptible(&chan->wq); +} + +static irqreturn_t phytium_lpc_snoop_irq(int irq, void *arg) +{ + struct phytium_lpc_snoop *lpc_snoop = arg; + u32 reg, data; + + if (regmap_read(lpc_snoop->regmap, snp_status_reg, ®)) + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ + reg &= (snp_status_reg_snp1_int | snp_status_reg_snp2_int); + if (!reg) + return IRQ_NONE; + + /* Ack pending IRQs */ + regmap_write(lpc_snoop->regmap, snp_status_reg, reg); + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(lpc_snoop->regmap, snp_data_reg, &data); + + if (reg & snp_status_reg_snp1_int) { + u8 val = (data & snp_data_reg_snp1_data_reg) >> snp_data_reg_snp1_shift; + + put_fifo_with_discard(&lpc_snoop->chan[0], val); + } + if (reg & snp_status_reg_snp2_int) { + u8 val = (data & snp_data_reg_snp2_data_reg) >> snp_data_reg_snp2_shift; + + put_fifo_with_discard(&lpc_snoop->chan[1], val); + } + + return IRQ_HANDLED; +} + +static int phytium_lpc_snoop_config_irq(struct phytium_lpc_snoop *lpc_snoop, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + lpc_snoop->irq = platform_get_irq(pdev, 0); + if (!lpc_snoop->irq) + return -ENODEV; + + rc = devm_request_irq(dev, lpc_snoop->irq, + phytium_lpc_snoop_irq, IRQF_SHARED, + DEVICE_NAME, lpc_snoop); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq); + lpc_snoop->irq = 0; + return rc; + } + + return 0; +} + +static int phytium_lpc_enable_snoop(struct phytium_lpc_snoop *lpc_snoop, + struct device *dev, + int channel, u16 lpc_port) +{ + int rc = 0; + u32 snp_enable_reg_en, snp_addr_reg_mask, snp_addr_reg_shift; + init_waitqueue_head(&lpc_snoop->chan[channel].wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, + SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_snoop->chan[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; + lpc_snoop->chan[channel].miscdev.parent = dev; + rc = misc_register(&lpc_snoop->chan[channel].miscdev); + if (rc) + return rc; + + /* Enable LPC snoop channel at requested port */ + switch (channel) { + case 0: + snp_enable_reg_en = snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en; + snp_addr_reg_mask = snp_addr_reg_snp1_addr; + snp_addr_reg_shift = snp_addr_reg_snp1_shift; + break; + case 1: + snp_enable_reg_en = snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en; + snp_addr_reg_mask = snp_addr_reg_snp2_addr; + snp_addr_reg_shift = snp_addr_reg_snp2_shift; + break; + default: + return -EINVAL; + } + + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, snp_enable_reg_en, snp_enable_reg_en); + regmap_update_bits(lpc_snoop->regmap, snp_addr_reg, snp_addr_reg_mask, + lpc_port << snp_addr_reg_shift); + return rc; +} + +static void phytium_lpc_disable_snoop(struct phytium_lpc_snoop *lpc_snoop, + int channel) +{ + switch (channel) { + case 0: + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, + snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, + 0); + break; + case 1: + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, + snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, + 0); + break; + default: + return; + } + + kfifo_free(&lpc_snoop->chan[channel].fifo); + misc_deregister(&lpc_snoop->chan[channel].miscdev); +} + +static int phytium_lpc_snoop_probe(struct platform_device *pdev) +{ + struct phytium_lpc_snoop *lpc_snoop; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL); + if (!lpc_snoop) + return -ENOMEM; + + lpc_snoop->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(lpc_snoop->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, lpc_snoop); + + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); + if (rc) { + dev_err(dev, "no snoop ports configured\n"); + return -ENODEV; + } + + rc = phytium_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) + return rc; + + rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 0, port); + if (rc) + return rc; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 1, port); + if (rc) + phytium_lpc_disable_snoop(lpc_snoop, 0); + } + + return rc; +} + +static int phytium_lpc_snoop_remove(struct platform_device *pdev) +{ + struct phytium_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + phytium_lpc_disable_snoop(lpc_snoop, 0); + phytium_lpc_disable_snoop(lpc_snoop, 1); + + return 0; +} + + +static const struct of_device_id phytium_lpc_snoop_match[] = { + { .compatible = "phytium,pe2201-lpc-snoop"}, + { }, + }; + +static struct platform_driver phytium_lpc_snoop_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = phytium_lpc_snoop_match, + }, + .probe = phytium_lpc_snoop_probe, + .remove = phytium_lpc_snoop_remove, +}; + +module_platform_driver(phytium_lpc_snoop_driver); + +MODULE_DEVICE_TABLE(of, phytium_lpc_snoop_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lan Hengyu lanhengyu1395@phytium.com.cn"); +MODULE_DESCRIPTION("PE2201 driver to control Phytium LPC snoop functionality"); -- Gitee From c8b3f6223a5ba0a2920630ee9a7b8110b1e328e5 Mon Sep 17 00:00:00 2001 From: wangzc Date: Thu, 16 Mar 2023 18:08:49 +0800 Subject: [PATCH 02/41] jtag: Add phytium jtag driver Signed-off-by: wangzc --- arch/arm64/boot/dts/phytium/pe2201.dtsi | 9 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/jtag/Kconfig | 24 + drivers/jtag/Makefile | 2 + drivers/jtag/jtag-phytium.c | 603 ++++++++++++++++++++++++ drivers/jtag/jtag.c | 289 ++++++++++++ include/linux/jtag.h | 45 ++ include/uapi/linux/jtag.h | 196 ++++++++ 9 files changed, 1171 insertions(+) create mode 100644 drivers/jtag/Kconfig create mode 100644 drivers/jtag/Makefile create mode 100644 drivers/jtag/jtag-phytium.c create mode 100644 drivers/jtag/jtag.c create mode 100644 include/linux/jtag.h create mode 100644 include/uapi/linux/jtag.h diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index c89961985b..49721462ea 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -265,4 +265,13 @@ jpeg0: jpeg@32b32000 { phytium,ocm-buf-addr = <0x30c40000 0x30c60000>; status = "disabled"; }; + + jtag: jtag@28044000 { + compatible = "phytium,jtag-master"; + reg = <0x0 0x28044000 0x0 0x1000>; + interrupts = <0x0 0xa7 0x4>; + clocks = <0x8>; + clock-names = "phytium_jtag_clk"; + status = "disabled"; + }; }; diff --git a/drivers/Kconfig b/drivers/Kconfig index 8395bc5159..d73b80b468 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -221,4 +221,6 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/jtag/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index e1ce029d28..0bb54bfed5 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -186,3 +186,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ +obj-$(CONFIG_JTAG) += jtag/ diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig new file mode 100644 index 0000000000..9b3fcd1fc7 --- /dev/null +++ b/drivers/jtag/Kconfig @@ -0,0 +1,24 @@ +menuconfig JTAG + tristate "JTAG support" + default Y + help + This provides basic core functionality support for JTAG class devices. + Hardware that is equipped with a JTAG microcontroller can be supported + by using this driver's interfaces. This driver exposes a set of IOCTLs + to the user space for the following commands: + + DR: Performs an IEEE 1149.1 Data Register scan. + IR: Performs an IEEE 1149.1 Instruction Register scan. + +menuconfig JTAG_PHYTIUM + tristate "Phytium SoC JTAG Master controller support" + depends on JTAG + help + This provides a support for Phytium JTAG device, equipped on + Phytium SoC PE2201. Drivers allows programming of hardware devices, + connected to SoC through the JTAG interface. + + If you want this support, you should say Y here. + + To compile this driver as a module, choose M here: the module will + be called jtag-phytium. diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile new file mode 100644 index 0000000000..77e7cbe6db --- /dev/null +++ b/drivers/jtag/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_JTAG) += jtag.o +obj-$(CONFIG_JTAG_PHYTIUM) += jtag-phytium.o diff --git a/drivers/jtag/jtag-phytium.c b/drivers/jtag/jtag-phytium.c new file mode 100644 index 0000000000..529e110436 --- /dev/null +++ b/drivers/jtag/jtag-phytium.c @@ -0,0 +1,603 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * JTAG driver for the Phytium SoC + * + * Copyright (C) 2021 Phytium Technology Inc. + * Xu Wenkang + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_JTGM_CTL 0x000 +#define TMS_RST_SET BIT(1) +#define NTRST_SET BIT(0) + +#define REG_TCK_CTL 0x004 +#define REG_FSM_CTL 0x008 +#define REG_INT_CFG 0x00c +#define REG_DR_GEN_CTL 0x010 +#define REG_INT_CTL 0x014 + +#define INFIFO_CNT GENMASK(12, 8) + +#define REG_DR_CONFIG 0x018 + +#define SB_SET BIT(12) +#define DR_VALID BIT(8) +#define DR_RSP BIT(7) + +enum jtag_dr_state { + DR_NORSP_WITHEXIT = 0x2, + DR_NORSP_WITHOUTEXIT = 0x3, + SJTAG_DR_READ_REG_WITHEXIT = 0x4, + DR_RSP_WITHEXIT = 0x6, + DR_RSP_WITHOUTEXIT = 0x7, +}; + +#define REG_DR_OUT 0x01c +#define REG_DR_IN 0x020 +#define REG_IR_CONFIG 0x024 + +enum jtag_ir_state { + IR_WITHEXIT = 0x0, + IR_WITHOUTEXIT = 0x1, +}; + +#define REG_IR_OUT 0x028 +#define REG_IR_16_CONFIG 0x02c + +#define TCK_FREQ 5000000 +#define PHYTIUM_JTAG_TIMEOUT msecs_to_jiffies(1000) +#define JTAG_TCK_MIN_DIVISOR_MASK 2 +#define JTAG_TCK_MAX_DIVISOR_MASK 0xffff +#define JTAG_GET_TCK_DIVISOR(x) (x & 0xffff) + +#undef PHYTIUM_JTAG_DEBUG 1 + +#ifdef PHYTIUM_JTAG_DEBUG +#define JTAG_DBUG(fmt, args...) pr_info("%s() " fmt, __func__, ##args) +#else +#define JTAG_DBUG(fmt, args...) +#endif + +struct phytium_jtag_config { + u32 jtag_buff_len; +}; + +struct phytium_jtag_info { + void __iomem *reg_base; + struct device *dev; + struct phytium_jtag_config *config; + int irq; + + struct clk *clk; + u32 clkin; + + struct completion completion; + struct completion outfifo_empty_completion; + struct completion count_completion; + + int count_out; + volatile int count_in; + int expect_num; + + u32 infifo[15]; + u32 infifo_full[16]; +}; + +static inline u32 +phytium_jtag_read(struct phytium_jtag_info *phytium_jtag, u32 reg) +{ + int val; + + val = readl(phytium_jtag->reg_base + reg); + JTAG_DBUG("%x, val: %x\n", reg, val); + return val; +} + +static inline void +phytium_jtag_write(struct phytium_jtag_info *phytium_jtag, u32 val, u32 reg) +{ + JTAG_DBUG("%x, val: %x\n", reg, val); + writel(val, phytium_jtag->reg_base + reg); +} + +static void phytium_jtag_disable_irq(struct phytium_jtag_info *phytium_jtag) +{ + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_INT_CTL); + reg |= 0x200ca; + phytium_jtag_write(phytium_jtag, reg, REG_INT_CTL); +} + +static void phytium_jtag_enable_irq(struct phytium_jtag_info *phytium_jtag) +{ + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_INT_CTL); + reg &= ~0xca; + phytium_jtag_write(phytium_jtag, reg, REG_INT_CTL); +} + +static int phytium_jtag_set_freq(struct jtag *jtag, u32 freq) +{ + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + u32 div; + + div = DIV_ROUND_UP(phytium_jtag->clkin, freq); + + if (div < JTAG_TCK_MIN_DIVISOR_MASK) { + pr_warn("The actual frequency will slower than required\n"); + div = JTAG_TCK_MIN_DIVISOR_MASK; + } + + if (div > JTAG_TCK_MAX_DIVISOR_MASK) { + pr_warn("The actual frequency will faster than required\n"); + div = JTAG_TCK_MAX_DIVISOR_MASK; + } + + phytium_jtag_write(phytium_jtag, div, REG_TCK_CTL); + JTAG_DBUG("Operation freq = %d / %d\n", phytium_jtag->clkin, div); + return 0; +} + +static int phytium_jtag_get_freq(struct jtag *jtag, u32 *freq) +{ + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + + *freq = phytium_jtag->clkin / + (JTAG_GET_TCK_DIVISOR(phytium_jtag_read(phytium_jtag, REG_TCK_CTL))); + + return 0; +} + +static int phytium_jtag_status_set(struct jtag *jtag, + struct jtag_end_tap_state *endstate) +{ + return 0; +} + +static int phytium_jtag_status_get(struct jtag *jtag, u32 *status) +{ + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + + *status = phytium_jtag_read(phytium_jtag, REG_FSM_CTL) && 0x70000; + return 0; +} + +static void phytium_hw_ir_16_scan_withexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits, u32 ir) +{ + u32 ir_16_config; + + ir_16_config = (0x1 << 24) + (IR_WITHEXIT << 21) + (0x1 << 20) + ((shift_bits - 0x1) << 16) + ir; + phytium_jtag_write(phytium_jtag, ir_16_config, REG_IR_16_CONFIG); +} + +static void phytium_hw_ir_scan_withoutexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 ir_config; + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_IR_16_CONFIG); + phytium_jtag_write(phytium_jtag, reg | 0x0 << 24, REG_IR_16_CONFIG); + + ir_config = (0x1 << 8) + (IR_WITHOUTEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, ir_config, REG_IR_CONFIG); +} + +static void phytium_hw_ir_scan_withexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 ir_config; + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_IR_16_CONFIG); + phytium_jtag_write(phytium_jtag, reg | 0x0 << 24, REG_IR_16_CONFIG); + + ir_config = (0x1 << 8) + (IR_WITHEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, ir_config, REG_IR_CONFIG); +} + +static void phytium_hw_dr_scan_withexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 dr_config; + + dr_config = (0x0 << 12) + (0x1 << 8) + (DR_RSP_WITHEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, dr_config, REG_DR_CONFIG); +} + +static void phytium_hw_dr_scan_withoutexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 dr_config; + + dr_config = (0x0 << 12) + (0x1 << 8) + (DR_RSP_WITHOUTEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, dr_config, REG_DR_CONFIG); +} + +static int phytium_hw_jtag_xfer(struct phytium_jtag_info *phytium_jtag, + struct jtag_xfer *xfer, u8 *xfer_data) +{ + unsigned int index = 0; + u32 shift_bits = 0; + u32 remain_xfer = xfer->length; + int i, tmp_idx = 0; + u32 fifo_reg = xfer->type ? REG_DR_OUT : REG_IR_OUT; + u32 *tdo; + u32 bytes; + u32 *xfer_data_32; + u32 outfifo_status; + int num; + + if (remain_xfer == 0) + return 0; + + bytes = DIV_ROUND_UP(xfer->length, BITS_PER_BYTE); + + tdo = kzalloc(bytes, GFP_KERNEL); + if (!tdo) + return -ENOMEM; + + xfer_data_32 = kzalloc(bytes, GFP_KERNEL); + if (!xfer_data_32) + return -ENOMEM; + + memcpy(xfer_data_32, xfer_data, bytes); + + while (remain_xfer) { + phytium_jtag->count_in = 0; + phytium_jtag_disable_irq(phytium_jtag); + if (remain_xfer > phytium_jtag->config->jtag_buff_len ) { + shift_bits = phytium_jtag->config->jtag_buff_len; + tmp_idx = shift_bits / 32; + for (i = 0; i < tmp_idx - 1; i++) { + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SIR_XFER) + phytium_hw_ir_scan_withoutexit(phytium_jtag, 32); + else + phytium_hw_dr_scan_withoutexit(phytium_jtag, 32); + } + phytium_jtag_write(phytium_jtag, xfer_data_32[index + tmp_idx - 1], fifo_reg); + if (xfer->type == JTAG_SIR_XFER) + phytium_hw_ir_scan_withexit(phytium_jtag, 32); + else + phytium_hw_dr_scan_withexit(phytium_jtag, 32); + } else { + shift_bits = remain_xfer; + if ((shift_bits > 32) && (shift_bits % 32 != 0)) { + tmp_idx = shift_bits / 32; + for (i = 0; i < tmp_idx; i++) { + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SIR_XFER) + phytium_hw_ir_scan_withoutexit(phytium_jtag, 32); + else + phytium_hw_dr_scan_withoutexit(phytium_jtag, 32); + } + if (xfer->type == JTAG_SIR_XFER) { + if (shift_bits <= 16) { + phytium_hw_ir_16_scan_withexit(phytium_jtag, shift_bits, xfer_data_32[index+i]); + } else { + phytium_jtag_write(phytium_jtag, xfer_data_32[index+i], fifo_reg); + phytium_hw_ir_scan_withexit(phytium_jtag, shift_bits % 32); + } + } else { + phytium_jtag_write(phytium_jtag, ((unsigned int *)xfer_data_32)[index+i], fifo_reg); + phytium_hw_dr_scan_withexit(phytium_jtag, shift_bits % 32); + } + } + + if (0 < shift_bits && shift_bits <= 32) { + if (xfer->type == JTAG_SIR_XFER) { + if (shift_bits <= 16) { + phytium_hw_ir_16_scan_withexit(phytium_jtag, shift_bits, xfer_data_32[index]); + } else { + phytium_jtag_write(phytium_jtag, xfer_data_32[index], fifo_reg); + phytium_hw_ir_scan_withexit(phytium_jtag, shift_bits); + } + } else { + phytium_jtag_write(phytium_jtag, xfer_data_32[index], fifo_reg); + phytium_hw_dr_scan_withexit(phytium_jtag, shift_bits); + } + } + + if ((shift_bits > 32) && (shift_bits % 32 == 0)) { + tmp_idx = shift_bits / 32; + for (i = 0; i < tmp_idx - 1; i++) { + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SDR_XFER) + phytium_hw_dr_scan_withoutexit(phytium_jtag, 32); + else + phytium_hw_ir_scan_withoutexit(phytium_jtag, 32); + } + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SDR_XFER) + phytium_hw_dr_scan_withexit(phytium_jtag, 32); + else + phytium_hw_ir_scan_withexit(phytium_jtag, 32); + } + } + + remain_xfer = remain_xfer - shift_bits; + + phytium_jtag_enable_irq(phytium_jtag); + + if (xfer->direction & JTAG_READ_XFER) { + tmp_idx = shift_bits / 32; + if (shift_bits % 32) + tmp_idx += 1; + phytium_jtag->expect_num = tmp_idx; + + if (phytium_jtag->count_in < tmp_idx) + wait_for_completion_interruptible_timeout(&phytium_jtag->completion, PHYTIUM_JTAG_TIMEOUT); + if (phytium_jtag->count_in == tmp_idx) + JTAG_DBUG("done %d : %d\n", phytium_jtag->count_in, tmp_idx); + + for (i = 0; i < tmp_idx; i++) { + if (shift_bits < 32) + tdo[index + i] = phytium_jtag->infifo[i]; + else + tdo[index + i] = phytium_jtag->infifo[i]; + shift_bits -= 32; + } + } + phytium_jtag_disable_irq(phytium_jtag); + index += tmp_idx; + + if (bytes % 4) + bytes = (bytes / 4 + 1) * 4; + memcpy(xfer_data_32, tdo, bytes); + memcpy(xfer_data, xfer_data_32, bytes); + } + + kfree(tdo); + kfree(xfer_data_32); + + return 0; +} + +static int phytium_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer, + u8 *xfer_data) +{ + int ret; + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + + ret = phytium_hw_jtag_xfer(phytium_jtag, xfer, xfer_data); + return ret; +} + +static irqreturn_t phytium_jtag_isr(int irq, void *dev_id) +{ + u32 status; + struct phytium_jtag_info *phytium_jtag = dev_id; + int i = 0; + int infifo_num, outfifo_num; + + status = phytium_jtag_read(phytium_jtag, REG_INT_CTL); + + infifo_num = (status & 0x1f00) >> 8; + outfifo_num = (status & 0x3f00000) >> 20; + + //JTAG_DBUG("irq:%x in:%d out:%d", status, infifo_num, outfifo_num); + /* The infifo is full and reach 16 */ + if (infifo_num == 16) { + for (i = 0; i < 16; i++) + phytium_jtag->infifo_full[i] = phytium_jtag_read(phytium_jtag, REG_DR_IN); + phytium_jtag->count_in += 16; + return IRQ_HANDLED; + } + + /* The infifo reached threshold BUF_SIZE:15 */ + if (status & 0x20) { + for (i = 0; i < 15; i++) + phytium_jtag->infifo[i] = phytium_jtag_read(phytium_jtag, REG_DR_IN); + phytium_jtag->count_in += 15; + complete(&phytium_jtag->completion); + return IRQ_HANDLED; + } + + /* The infifo timeout interrupt */ + if (status & 0x10) { + for(i = 0; i < infifo_num; i++) + phytium_jtag->infifo[i + phytium_jtag->count_in] = phytium_jtag_read(phytium_jtag, REG_DR_IN); + + phytium_jtag->count_in += infifo_num; + + if (phytium_jtag->count_in == phytium_jtag->expect_num) { + complete(&phytium_jtag->completion); + //printk("irq timeout comp\n"); + } + return IRQ_HANDLED; + } + + if (((status & 0x10000) == 0x10000) && ((status & 0x20000) != 0x20000)) { + phytium_jtag_write(phytium_jtag, status | 0x20000, REG_INT_CTL); + return IRQ_HANDLED; + } + + pr_err("TODO Check JTAG's interrupt %x\n", + phytium_jtag_read(phytium_jtag, REG_INT_CTL)); + return IRQ_NONE; +} + + +static struct phytium_jtag_config jtag_config = { + .jtag_buff_len = 15 * 32, +}; + +static const struct of_device_id phytium_jtag_of_matches[] = { + { + .compatible = "phytium,jtag-master", + .data = &jtag_config, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_jtag_of_matches); + +static int phytium_jtag_bitbang(struct jtag *jtag, + struct tck_bitbang *tck_bitbang) +{ + return 0; +} + +static int phytium_jtag_mode_set(struct jtag *jtag, struct jtag_mode *jtag_mode) +{ + return 0; +} + +static int phytium_jtag_enable(struct jtag *jtag) +{ + return 0; +} + +static int phytium_jtag_disable(struct jtag *jtag) +{ + return 0; +} + +static const struct jtag_ops phytium_jtag_ops = { + .freq_get = phytium_jtag_get_freq, + .freq_set = phytium_jtag_set_freq, + .status_get = phytium_jtag_status_get, + .status_set = phytium_jtag_status_set, + .xfer = phytium_jtag_xfer, + .mode_set = phytium_jtag_mode_set, + .bitbang = phytium_jtag_bitbang, + .enable = phytium_jtag_enable, + .disable = phytium_jtag_disable, +}; + +static int phytium_jtag_probe(struct platform_device *pdev) +{ + struct phytium_jtag_info *phytium_jtag; + struct jtag *jtag; + const struct of_device_id *jtag_dev_id; + struct resource *res; + int ret = 0; + + jtag = jtag_alloc(&pdev->dev, sizeof(*phytium_jtag), + &phytium_jtag_ops); + if (!jtag) + return -ENOMEM; + + platform_set_drvdata(pdev, jtag); + phytium_jtag = jtag_priv(jtag); + phytium_jtag->dev = &pdev->dev; + + jtag_dev_id = of_match_device(phytium_jtag_of_matches, &pdev->dev); + if (!jtag_dev_id) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out; + } + + phytium_jtag->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (!phytium_jtag->reg_base) { + ret = -EIO; + goto out; + } + + phytium_jtag->irq = platform_get_irq(pdev, 0); + if (phytium_jtag->irq < 0) { + dev_err(&pdev->dev, "no irq specified\n"); + ret = -ENOENT; + goto out; + } + + phytium_jtag->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(phytium_jtag->clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + return -ENODEV; + } + + phytium_jtag->clk = devm_clk_get(&pdev->dev, NULL); + phytium_jtag->clkin = clk_get_rate(phytium_jtag->clk); + dev_info(&pdev->dev, "phytium_jtag->clkin %d\n", phytium_jtag->clkin); + + phytium_jtag->config = (struct phytium_jtag_config *)jtag_dev_id->data; + + init_completion(&phytium_jtag->completion); + init_completion(&phytium_jtag->count_completion); + + phytium_jtag->count_in = 0; + phytium_jtag->expect_num = 0; + + //Jtag Master init + phytium_jtag_write(phytium_jtag, 0x3, REG_JTGM_CTL); + udelay(3); + + ret = devm_request_irq(&pdev->dev, phytium_jtag->irq, phytium_jtag_isr, + 0, dev_name(&pdev->dev), phytium_jtag); + if (ret) { + dev_dbg(&pdev->dev, "JTAG Unable to get IRQ"); + goto out; + } + + phytium_jtag_set_freq(jtag, TCK_FREQ); + + phytium_jtag_write(phytium_jtag, 0x81032, REG_INT_CFG); + + // Register a misc device + ret = devm_jtag_register(phytium_jtag->dev, jtag); + if (ret) + { + dev_err(&pdev->dev, "failed to create device\n"); + goto out; + } + + dev_info(&pdev->dev, "phytium_jtag master: driver successfully loaded.\n"); + + return 0; + +out: + phytium_jtag_write(phytium_jtag, 0x3, REG_JTGM_CTL); + kfree(jtag); + dev_warn(&pdev->dev, "phytium_jtag: driver init failed (ret=%d)!\n", + ret); + return ret; +} + +static int phytium_jtag_remove(struct platform_device *pdev) +{ + struct jtag *jtag = platform_get_drvdata(pdev); + struct phytium_jtag_info *phytium_jtag; + + if (!jtag) + return 0; + + phytium_jtag = jtag_priv(jtag); + phytium_jtag_write(phytium_jtag, 0x3, REG_JTGM_CTL); + jtag_free(jtag); + dev_info(&pdev->dev, "phytium-jtag-master remove"); + return 0; +} + +static struct platform_driver phytium_jtag_driver = { + .probe = phytium_jtag_probe, + .remove = phytium_jtag_remove, + .driver = { + .name = "phytium-jtag", + .of_match_table = phytium_jtag_of_matches, + }, +}; + +module_platform_driver(phytium_jtag_driver); + +MODULE_AUTHOR("Xu Wenkang "); +MODULE_DESCRIPTION("Phytium JTAG Master Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c new file mode 100644 index 0000000000..876f759b33 --- /dev/null +++ b/drivers/jtag/jtag.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018 Mellanox Technologies. All rights reserved. +// Copyright (c) 2018 Oleksandr Shamray +// Copyright (c) 2019 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct jtag { + struct miscdevice miscdev; + const struct jtag_ops *ops; + int id; + unsigned long priv[0]; +}; + +static DEFINE_IDA(jtag_ida); + +void *jtag_priv(struct jtag *jtag) +{ + return jtag->priv; +} +EXPORT_SYMBOL_GPL(jtag_priv); + +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct jtag *jtag = file->private_data; + struct jtag_end_tap_state endstate; + struct jtag_xfer xfer; + struct tck_bitbang bitbang; + struct jtag_mode mode; + + u8 *xfer_data; + u32 data_size; + u32 value; + int err; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case JTAG_GIOCFREQ: + if (!jtag->ops->freq_get) + return -EOPNOTSUPP; + + err = jtag->ops->freq_get(jtag, &value); + if (err) + break; + + if (put_user(value, (__u32 __user *)arg)) + err = -EFAULT; + break; + + case JTAG_SIOCFREQ: + if (!jtag->ops->freq_set) + return -EOPNOTSUPP; + + if (get_user(value, (__u32 __user *)arg)) + return -EFAULT; + if (value == 0) + return -EINVAL; + + err = jtag->ops->freq_set(jtag, value); + break; + + case JTAG_SIOCSTATE: + if (copy_from_user(&endstate, (const void __user *)arg, sizeof(struct jtag_end_tap_state))) + return -EFAULT; + if (endstate.endstate > JTAG_STATE_UPDATEIR) + return -EFAULT; + if (endstate.reset > JTAG_FORCE_RESET) + return -EINVAL; + err = jtag->ops->status_set(jtag, &endstate); + break; + + case JTAG_IOCXFER: + if (copy_from_user(&xfer, (const void __user *)arg, sizeof(struct jtag_xfer))) + return -EFAULT; + if (xfer.length >= JTAG_MAX_XFER_DATA_LEN) + return -EINVAL; + if (xfer.type > JTAG_SDR_XFER) + return -EINVAL; + if (xfer.direction > JTAG_READ_WRITE_XFER) + return -EINVAL; + + data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE); + xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size); + + if (IS_ERR(xfer_data)) + return -EFAULT; + + err = jtag->ops->xfer(jtag, &xfer, xfer_data); + if (err) { + kfree(xfer_data); + return err; + } + + err = copy_to_user(u64_to_user_ptr(xfer.tdio), (void *)xfer_data, data_size); + kfree(xfer_data); + if (err) + return -EFAULT; + + if (copy_to_user((void __user *)arg, (void *)&xfer, sizeof(struct jtag_xfer))) + return -EFAULT; + + break; + + case JTAG_GIOCSTATUS: + err = jtag->ops->status_get(jtag, &value); + if (err) + break; + + err = put_user(value, (__u32 __user *)arg); + break; + + case JTAG_IOCBITBANG: + if (copy_from_user(&bitbang, (const void __user *)arg, sizeof(struct tck_bitbang))) + return -EFAULT; + + err = jtag->ops->bitbang(jtag, &bitbang); + if (err) + break; + + if (copy_to_user((void __user *)arg, (void *)&bitbang, sizeof(struct tck_bitbang))) + return -EFAULT; + break; + + case JTAG_SIOCMODE: + if (!jtag->ops->mode_set) + return -EOPNOTSUPP; + + if (copy_from_user(&mode, (const void __user *)arg, sizeof(struct jtag_mode))) + return -EFAULT; + err = jtag->ops->mode_set(jtag, &mode); + break; + + default: + return -EINVAL; + } + return err; +} + +static int jtag_open(struct inode *inode, struct file *file) +{ + struct jtag *jtag = container_of(file->private_data, struct jtag, miscdev); + + file->private_data = jtag; + if (jtag->ops->enable(jtag)) + return -EBUSY; + return nonseekable_open(inode, file); +} + +static int jtag_release(struct inode *inode, struct file *file) +{ + struct jtag *jtag = file->private_data; + + if (jtag->ops->disable(jtag)) + return -EBUSY; + + return 0; +} + +static const struct file_operations jtag_fops = { + .owner = THIS_MODULE, + .open = jtag_open, + .llseek = noop_llseek, + .unlocked_ioctl = jtag_ioctl, + .release = jtag_release, +}; + +struct jtag *jtag_alloc(struct device *host, size_t priv_size, + const struct jtag_ops *ops) +{ + struct jtag *jtag; + + if (!host) + return NULL; + + if (!ops) + return NULL; + + if (!ops->status_set ||!ops->status_get || !ops->xfer) + return NULL; + + jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL); + if (!jtag) + return NULL; + + jtag->ops = ops; + jtag->miscdev.parent = host; + + return jtag; +} +EXPORT_SYMBOL_GPL(jtag_alloc); + +void jtag_free(struct jtag *jtag) +{ + kfree(jtag); +} +EXPORT_SYMBOL_GPL(jtag_free); + +static int jtag_register(struct jtag *jtag) +{ + struct device *dev = jtag->miscdev.parent; + int err; + int id; + + if (!dev) + return -ENODEV; + + id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return id; + + jtag->id = id; + + jtag->miscdev.fops = &jtag_fops; + jtag->miscdev.minor = MISC_DYNAMIC_MINOR; + + jtag->miscdev.name = kasprintf(GFP_KERNEL, "jtag%d", id); + if (!jtag->miscdev.name) { + err = -ENOMEM; + goto err_jtag_alloc; + } + + err = misc_register(&jtag->miscdev); + if (err) { + dev_err(jtag->miscdev.parent, "Unable to register device\n"); + goto err_jtag_name; + } + return 0; + +err_jtag_name: + kfree(jtag->miscdev.name); +err_jtag_alloc: + ida_simple_remove(&jtag_ida, id); + return err; +} + +static void jtag_unregister(struct jtag *jtag) +{ + misc_deregister(&jtag->miscdev); + kfree(jtag->miscdev.name); + ida_simple_remove(&jtag_ida, jtag->id); +} + +static void devm_jtag_unregister(struct device *dev, void *res) +{ + jtag_unregister(*(struct jtag **)res); +} + +int devm_jtag_register(struct device *dev, struct jtag *jtag) +{ + struct jtag **ptr; + int ret; + + ptr = devres_alloc(devm_jtag_unregister, sizeof(struct jtag *), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = jtag_register(jtag); + if (!ret) { + *ptr = jtag; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + return ret; +} +EXPORT_SYMBOL_GPL(devm_jtag_register); + +static void __exit jtag_exit(void) +{ + ida_destroy(&jtag_ida); +} + +module_exit(jtag_exit); + +MODULE_AUTHOR("Xu Wenkang "); +MODULE_DESCRIPTION("Generic jtag support"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/jtag.h b/include/linux/jtag.h new file mode 100644 index 0000000000..d977571142 --- /dev/null +++ b/include/linux/jtag.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ +/* Copyright (c) 2018 Oleksandr Shamray */ +/* Copyright (c) 2019 Intel Corporation */ +/* Copyright (c) 2021 Phytium Corporation */ + +#ifndef __LINUX_JTAG_H +#define __LINUX_JTAG_H + +#include +#include + +#define JTAG_MAX_XFER_DATA_LEN 65535 + +struct jtag; + +/** + * struct jtag_ops - callbacks for JTAG control functions: + * + * @freq_get: get frequency function. Filled by dev driver + * @freq_set: set frequency function. Filled by dev driver + * @status_get: get JTAG TAPC state function. Mandatory, Filled by dev driver + * @status_set: set JTAG TAPC state function. Mandatory, Filled by dev driver + * @xfer: send JTAG xfer function. Mandatory func. Filled by dev driver + * @mode_set: set + */ +struct jtag_ops { + int (*freq_get)(struct jtag *jtag, u32 *freq); + int (*freq_set)(struct jtag *jtag, u32 freq); + int (*status_get)(struct jtag *jtag, u32 *state); + int (*status_set)(struct jtag *jtag, struct jtag_end_tap_state *endst); + int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data); + int (*mode_set)(struct jtag *jtag, struct jtag_mode *jtag_mode); + int (*bitbang)(struct jtag *jtag, struct tck_bitbang *tck_bitbang); + int (*enable)(struct jtag *jtag); + int (*disable)(struct jtag *jtag); +}; + +extern void *jtag_priv(struct jtag *jtag); +extern int devm_jtag_register(struct device *dev, struct jtag *jtag); +extern struct jtag *jtag_alloc(struct device *host, size_t priv_size, + const struct jtag_ops *ops); +extern void jtag_free(struct jtag *jtag); + +#endif /* __LINUX_JTAG_H */ diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h new file mode 100644 index 0000000000..96334bbcc6 --- /dev/null +++ b/include/uapi/linux/jtag.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ +/* Copyright (c) 2018 Oleksandr Shamray */ +/* Copyright (c) 2019 Intel Corporation */ + +#ifndef __UAPI_LINUX_JTAG_H +#define __UAPI_LINUX_JTAG_H + +/* + * JTAG_XFER_MODE: JTAG transfer mode. Used to set JTAG controller transfer mode + * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_MODE 0 +/* + * JTAG_CONTROL_MODE: JTAG controller mode. Used to set JTAG controller mode + * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_CONTROL_MODE 1 +/* + * JTAG_SLAVE_MODE: JTAG master mode output disable, it is used to + * enable other devices to own the JTAG bus. + * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_SLAVE_MODE 0 +/* + * JTAG_MASTER_MODE: JTAG master mode. Used to set JTAG controller master mode + * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_MASTER_MODE 1 +/* + * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang + * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_HW_MODE 1 +/* + * JTAG_XFER_SW_MODE: JTAG software mode. Used to set SW drived or bitbang + * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_SW_MODE 0 + +/** + * enum jtag_endstate: + * + * @JTAG_STATE_TLRESET: JTAG state machine Test Logic Reset state + * @JTAG_STATE_IDLE: JTAG state machine IDLE state + * @JTAG_STATE_SELECTDR: JTAG state machine SELECT_DR state + * @JTAG_STATE_CAPTUREDR: JTAG state machine CAPTURE_DR state + * @JTAG_STATE_SHIFTDR: JTAG state machine SHIFT_DR state + * @JTAG_STATE_EXIT1DR: JTAG state machine EXIT-1 DR state + * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state + * @JTAG_STATE_EXIT2DR: JTAG state machine EXIT-2 DR state + * @JTAG_STATE_UPDATEDR: JTAG state machine UPDATE DR state + * @JTAG_STATE_SELECTIR: JTAG state machine SELECT_IR state + * @JTAG_STATE_CAPTUREIR: JTAG state machine CAPTURE_IR state + * @JTAG_STATE_SHIFTIR: JTAG state machine SHIFT_IR state + * @JTAG_STATE_EXIT1IR: JTAG state machine EXIT-1 IR state + * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state + * @JTAG_STATE_EXIT2IR: JTAG state machine EXIT-2 IR state + * @JTAG_STATE_UPDATEIR: JTAG state machine UPDATE IR state + */ +enum jtag_endstate { + JTAG_STATE_RESET, + JTAG_STATE_IDLE, + JTAG_STATE_SELECTDR, + JTAG_STATE_CAPTUREDR, + JTAG_STATE_SHIFTDR, + JTAG_STATE_EXIT1DR, + JTAG_STATE_PAUSEDR, + JTAG_STATE_EXIT2DR, + JTAG_STATE_UPDATEDR, + JTAG_STATE_SELECTIR, + JTAG_STATE_CAPTUREIR, + JTAG_STATE_SHIFTIR, + JTAG_STATE_EXIT1IR, + JTAG_STATE_PAUSEIR, + JTAG_STATE_EXIT2IR, + JTAG_STATE_UPDATEIR +}; + +/** + * enum jtag_reset: + * + * @JTAG_NO_RESET: JTAG run TAP from current state + * @JTAG_FORCE_RESET: JTAG force TAP to reset state + */ +enum jtag_reset { + JTAG_NO_RESET = 0, + JTAG_FORCE_RESET = 1, +}; + +/** + * enum jtag_xfer_type: + * + * @JTAG_SIR_XFER: SIR transfer + * @JTAG_SDR_XFER: SDR transfer + */ +enum jtag_xfer_type { + JTAG_SIR_XFER = 0, + JTAG_SDR_XFER = 1, +}; + +/** + * enum jtag_xfer_direction: + * + * @JTAG_READ_XFER: read transfer + * @JTAG_WRITE_XFER: write transfer + * @JTAG_READ_WRITE_XFER: read & write transfer + */ +enum jtag_xfer_direction { + JTAG_READ_XFER = 1, + JTAG_WRITE_XFER = 2, + JTAG_READ_WRITE_XFER = 3, +}; + +/** + * struct jtag_end_tap_state - forces JTAG state machine to go into a TAPC + * state + * + * @reset: 0 - run IDLE/PAUSE from current state + * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE + * @end: completion flag + * @tck: clock counter + * + * Structure provide interface to JTAG device for JTAG set state execution. + */ +struct jtag_end_tap_state { + __u8 reset; + __u8 endstate; + __u8 tck; +}; + +/** + * struct jtag_xfer - jtag xfer: + * + * @type: transfer type + * @direction: xfer direction + * @length: xfer bits length + * @tdio : xfer data array + * @endir: xfer end state + * + * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution. + */ +struct jtag_xfer { + __u8 type; + __u8 direction; + __u8 endstate; + __u8 padding; + __u32 length; + __u64 tdio; +}; + +/** + * struct jtag_bitbang - jtag bitbang: + * + * @tms: JTAG TMS + * @tdi: JTAG TDI (input) + * @tdo: JTAG TDO (output) + * + * Structure provide interface to JTAG device for JTAG bitbang execution. + */ + +struct tck_bitbang { + __u8 tms; + __u8 tdi; + __u8 tdo; +} __attribute__((__packed__)); + +/** + * struct jtag_mode - jtag mode: + * + * @feature: 0 - JTAG feature setting selector for JTAG controller HW/SW + * 1 - JTAG feature setting selector for controller bus master + * mode output (enable / disable). + * @mode: (0 - SW / 1 - HW) for JTAG_XFER_MODE feature(0) + * (0 - output disable / 1 - output enable) for JTAG_CONTROL_MODE + * feature(1) + * + * Structure provide configuration modes to JTAG device. + */ +struct jtag_mode { + __u32 feature; + __u32 mode; +}; + +/* ioctl interface */ +#define __JTAG_IOCTL_MAGIC 0xb2 + +#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_end_tap_state) +#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int) +#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int) +#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer) +#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate) +#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int) +#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int) + +#endif /* __UAPI_LINUX_JTAG_H */ -- Gitee From 17c2c91fa4e809dbed475c99a6098f30c2ebf244 Mon Sep 17 00:00:00 2001 From: bmcadmin Date: Tue, 18 Jul 2023 11:13:21 +0800 Subject: [PATCH 03/41] ipmi: Add ipmb_dev_int driver Signed-off-by: bmcadmin --- drivers/char/ipmi/Kconfig | 9 + drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/ipmb_dev_int.c | 389 +++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+) create mode 100755 drivers/char/ipmi/ipmb_dev_int.c diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 9f99107994..5a01c66341 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -147,3 +147,12 @@ config ASPEED_BT_IPMI_BMC Provides a driver for the BT (Block Transfer) IPMI interface found on Aspeed SOCs (AST2400 and AST2500). The driver implements the BMC side of the BT interface. + +config IPMB_DEVICE_INTERFACE + tristate "IPMB Interface handler" + depends on I2C + depends on I2C_SLAVE + help + Provides a dirver for a device (Satellite MC) to + receive requests and send responses back to the BMC via + the IPMB interface. This module requires I2C support. diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index e945653131..f3903a95ae 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o obj-$(CONFIG_PHYTIUM_KCS_IPMI_BMC) += kcs_bmc_phytium.o obj-$(CONFIG_PHYTIUM_BT_IPMI_BMC) += bt_bmc_phytium.o +obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c new file mode 100755 index 0000000000..10765262f6 --- /dev/null +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * IPMB driver to receive a request and send a response + * + * Copyright (C) 2019 Mellanox Techologies, Ltd. + * + * This was inspired by Brendan Higgins' ipmi-bmc-bt-i2c driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MSG_LEN 240 +#define IPMB_REQUEST_LEN_MIN 7 +#define NETFN_RSP_BIT_MASK 0x4 +#define REQUEST_QUEUE_MAX_LEN 256 + +#define IPMB_MSG_LEN_IDX 0 +#define RQ_SA_8BIT_IDX 1 +#define NETFN_LUN_IDX 2 + +#define GET_7BIT_ADDR(addr_8bit) (addr_8bit >> 1) +#define GET_8BIT_ADDR(addr_7bit) ((addr_7bit << 1) & 0xff) + +#define IPMB_MSG_PAYLOAD_LEN_MAX (MAX_MSG_LEN - IPMB_REQUEST_LEN_MIN - 1) + +#define SMBUS_MSG_HEADER_LENGTH 2 +#define SMBUS_MSG_IDX_OFFSET (SMBUS_MSG_HEADER_LENGTH + 1) + +struct ipmb_msg { + u8 len; + u8 rs_sa; + u8 netfn_rs_lun; + u8 checksum1; + u8 rq_sa; + u8 rq_seq_rq_lun; + u8 cmd; + u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX]; + /* checksum2 is included in payload */ +} __packed; + +struct ipmb_request_elem { + struct list_head list; + struct ipmb_msg request; +}; + +struct ipmb_dev { + struct i2c_client *client; + struct miscdevice miscdev; + struct ipmb_msg request; + struct list_head request_queue; + atomic_t request_queue_len; + size_t msg_idx; + spinlock_t lock; + wait_queue_head_t wait_queue; + struct mutex file_mutex; + bool is_i2c_protocol; +}; + +static inline struct ipmb_dev *to_ipmb_dev(struct file *file) +{ + return container_of(file->private_data, struct ipmb_dev, miscdev); +} + +static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + struct ipmb_request_elem *queue_elem; + struct ipmb_msg msg; + ssize_t ret = 0; + + memset(&msg, 0, sizeof(msg)); + + spin_lock_irq(&ipmb_dev->lock); + + while (list_empty(&ipmb_dev->request_queue)) { + spin_unlock_irq(&ipmb_dev->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(ipmb_dev->wait_queue, + !list_empty(&ipmb_dev->request_queue)); + if (ret) + return ret; + + spin_lock_irq(&ipmb_dev->lock); + } + + queue_elem = list_first_entry(&ipmb_dev->request_queue, + struct ipmb_request_elem, list); + memcpy(&msg, &queue_elem->request, sizeof(msg)); + list_del(&queue_elem->list); + kfree(queue_elem); + atomic_dec(&ipmb_dev->request_queue_len); + + spin_unlock_irq(&ipmb_dev->lock); + + count = min_t(size_t, count, msg.len + 1); + if (copy_to_user(buf, &msg, count)) + ret = -EFAULT; + + return ret < 0 ? ret : count; +} + +static int ipmb_i2c_write(struct i2c_client *client, u8 *msg, u8 addr) +{ + struct i2c_msg i2c_msg; + + /* + * subtract 1 byte (rq_sa) from the length of the msg passed to + * raw i2c_transfer + */ + i2c_msg.len = msg[IPMB_MSG_LEN_IDX] - 1; + + /* Assign message to buffer except first 2 bytes (length and address) */ + i2c_msg.buf = msg + 2; + + i2c_msg.addr = addr; + i2c_msg.flags = client->flags & I2C_CLIENT_PEC; + + return i2c_transfer(client->adapter, &i2c_msg, 1); +} + +static ssize_t ipmb_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + u8 rq_sa, netf_rq_lun, msg_len; + struct i2c_client *temp_client; + u8 msg[MAX_MSG_LEN]; + ssize_t ret; + + if (count > sizeof(msg)) + return -EINVAL; + + if (copy_from_user(&msg, buf, count)) + return -EFAULT; + + if (count < msg[0]) + return -EINVAL; + + rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); + netf_rq_lun = msg[NETFN_LUN_IDX]; + + /* Check i2c block transfer vs smbus */ + if (ipmb_dev->is_i2c_protocol) { + ret = ipmb_i2c_write(ipmb_dev->client, msg, rq_sa); + return (ret == 1) ? count : ret; + } + + /* + * subtract rq_sa and netf_rq_lun from the length of the msg. Fill the + * temporary client. Note that its use is an exception for IPMI. + */ + msg_len = msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH; + temp_client = kmemdup(ipmb_dev->client, sizeof(*temp_client), GFP_KERNEL); + if (!temp_client) + return -ENOMEM; + + temp_client->addr = rq_sa; + + ret = i2c_smbus_write_block_data(temp_client, netf_rq_lun, msg_len, + msg + SMBUS_MSG_IDX_OFFSET); + kfree(temp_client); + + return ret < 0 ? ret : count; +} + +static __poll_t ipmb_poll(struct file *file, poll_table *wait) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + __poll_t mask = EPOLLOUT; + + mutex_lock(&ipmb_dev->file_mutex); + poll_wait(file, &ipmb_dev->wait_queue, wait); + + if (atomic_read(&ipmb_dev->request_queue_len)) + mask |= EPOLLIN; + mutex_unlock(&ipmb_dev->file_mutex); + + return mask; +} + +static const struct file_operations ipmb_fops = { + .owner = THIS_MODULE, + .read = ipmb_read, + .write = ipmb_write, + .poll = ipmb_poll, +}; + +/* Called with ipmb_dev->lock held. */ +static void ipmb_handle_request(struct ipmb_dev *ipmb_dev) +{ + struct ipmb_request_elem *queue_elem; + + if (atomic_read(&ipmb_dev->request_queue_len) >= + REQUEST_QUEUE_MAX_LEN) + return; + + queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC); + if (!queue_elem) + return; + + memcpy(&queue_elem->request, &ipmb_dev->request, + sizeof(struct ipmb_msg)); + memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request)); + list_add(&queue_elem->list, &ipmb_dev->request_queue); + atomic_inc(&ipmb_dev->request_queue_len); + wake_up_all(&ipmb_dev->wait_queue); +} + +static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa) +{ + /* The 8 lsb of the sum is 0 when the checksum is valid */ + return (rs_sa + ipmb_dev->request.netfn_rs_lun + + ipmb_dev->request.checksum1); +} + +/* + * Verify if message has proper ipmb header with minimum length + * and correct checksum byte. + */ +static bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa) +{ + if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) && + (!ipmb_verify_checksum1(ipmb_dev, rs_sa))) + return true; + + return false; +} + +/* + * The IPMB protocol only supports I2C Writes so there is no need + * to support I2C_SLAVE_READ* events. + * This i2c callback function only monitors IPMB request messages + * and adds them in a queue, so that they can be handled by + * receive_ipmb_request. + */ +static int ipmb_slave_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); + u8 *buf = (u8 *)&ipmb_dev->request; + unsigned long flags; + + spin_lock_irqsave(&ipmb_dev->lock, flags); + switch (event) { + case I2C_SLAVE_WRITE_REQUESTED: + memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request)); + ipmb_dev->msg_idx = 0; + + /* + * At index 0, ipmb_msg stores the length of msg, + * skip it for now. + * The len will be populated once the whole + * buf is populated. + * + * The I2C bus driver's responsibility is to pass the + * data bytes to the backend driver; it does not + * forward the i2c slave address. + * Since the first byte in the IPMB message is the + * address of the responder, it is the responsibility + * of the IPMB driver to format the message properly. + * So this driver prepends the address of the responder + * to the received i2c data before the request message + * is handled in userland. + */ + buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr); + break; + + case I2C_SLAVE_WRITE_RECEIVED: + if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg) - 1){ + break; + } + buf[++ipmb_dev->msg_idx] = *val; + + break; + + case I2C_SLAVE_STOP: + ipmb_dev->request.len = ipmb_dev->msg_idx; + if (is_ipmb_msg(ipmb_dev, GET_8BIT_ADDR(client->addr))){ + ipmb_handle_request(ipmb_dev); + } + break; + + default: + break; + } + spin_unlock_irqrestore(&ipmb_dev->lock, flags); + + return 0; +} + +static int ipmb_probe(struct i2c_client *client) +{ + struct ipmb_dev *ipmb_dev; + int ret; + + ipmb_dev = devm_kzalloc(&client->dev, sizeof(*ipmb_dev), + GFP_KERNEL); + if (!ipmb_dev) + return -ENOMEM; + + spin_lock_init(&ipmb_dev->lock); + init_waitqueue_head(&ipmb_dev->wait_queue); + atomic_set(&ipmb_dev->request_queue_len, 0); + INIT_LIST_HEAD(&ipmb_dev->request_queue); + + mutex_init(&ipmb_dev->file_mutex); + + ipmb_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + + ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL, + "%s%d", "ipmb-", + client->adapter->nr); + ipmb_dev->miscdev.fops = &ipmb_fops; + ipmb_dev->miscdev.parent = &client->dev; + ret = misc_register(&ipmb_dev->miscdev); + if (ret) + return ret; + + ipmb_dev->is_i2c_protocol + = device_property_read_bool(&client->dev, "i2c-protocol"); + + ipmb_dev->client = client; + i2c_set_clientdata(client, ipmb_dev); + ret = i2c_slave_register(client, ipmb_slave_cb); + if (ret) { + misc_deregister(&ipmb_dev->miscdev); + return ret; + } + + return 0; +} + +static int ipmb_remove(struct i2c_client *client) +{ + struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); + + i2c_slave_unregister(client); + misc_deregister(&ipmb_dev->miscdev); + + return 0; +} + +static const struct i2c_device_id ipmb_id[] = { + { "ipmb-dev", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ipmb_id); + +static const struct acpi_device_id acpi_ipmb_id[] = { + { "IPMB0001", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id); +#ifdef CONFIG_OF +static const struct of_device_id phytium_ipmb_of_match[] = { + { .compatible = "ipmb-dev", }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_ipmb_of_match); +#endif +static struct i2c_driver ipmb_driver = { + .driver = { + .name = "ipmb-dev", + .of_match_table = of_match_ptr(phytium_ipmb_of_match), + .acpi_match_table = ACPI_PTR(acpi_ipmb_id), + }, + .probe_new = ipmb_probe, + .remove = ipmb_remove, + .id_table = ipmb_id, +}; +module_i2c_driver(ipmb_driver); + +MODULE_AUTHOR("Mellanox Technologies"); +MODULE_DESCRIPTION("IPMB driver"); +MODULE_LICENSE("GPL v2"); -- Gitee From 5bece36bffa7aeec8df83d525e456e5e25931147 Mon Sep 17 00:00:00 2001 From: Tian Jingzai Date: Wed, 15 Nov 2023 17:57:56 +0800 Subject: [PATCH 04/41] I2C: Update the i2c driver for Phytium Add code that supports driver master-slave switching. The code only supports one-on-one for IPMB functionality. Fix the issue of PE2201 transfer in smbus mode. using the command 'i2cget -f -y s' will make the cpu trap in stall. Signed-off-by: Tian Jingzai Signed-off-by: Wang Min --- drivers/i2c/busses/Makefile | 4 +- .../i2c/busses/i2c-phytium-chmd-platform.c | 373 ++++++++ drivers/i2c/busses/i2c-phytium-common.c | 164 ++-- drivers/i2c/busses/i2c-phytium-core.h | 423 +++++---- drivers/i2c/busses/i2c-phytium-mix.c | 891 ++++++++++++++++++ 5 files changed, 1594 insertions(+), 261 deletions(-) create mode 100644 drivers/i2c/busses/i2c-phytium-chmd-platform.c create mode 100644 drivers/i2c/busses/i2c-phytium-mix.c diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 0aea0dd31c..40b0e2bd15 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -54,9 +54,9 @@ obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o i2c-designware-pci-objs := i2c-designware-pcidrv.o obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o obj-$(CONFIG_I2C_PHYTIUM_CORE) += i2c-phytium-core.o -i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-master.o i2c-phytium-slave.o +i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-mix.o obj-$(CONFIG_I2C_PHYTIUM_PCI) += i2c-phytium-pci.o -obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-platform.o +obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-chmd-platform.o obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o diff --git a/drivers/i2c/busses/i2c-phytium-chmd-platform.c b/drivers/i2c/busses/i2c-phytium-chmd-platform.c new file mode 100644 index 0000000000..03356a7399 --- /dev/null +++ b/drivers/i2c/busses/i2c-phytium-chmd-platform.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium I2C adapter driver. + * + * Derived from Synopysys I2C driver. + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +#define DRV_NAME "i2c-phytium-platform" + +static u32 i2c_phytium_get_clk_rate_khz(struct phytium_i2c_dev *dev) +{ + return clk_get_rate(dev->clk) / 1000; +} + +#ifdef CONFIG_ACPI +static void phytium_i2c_acpi_params(struct platform_device *pdev, char method[], + u16 *hcnt, u16 *lcnt, u32 *sda_hold) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + union acpi_object *obj; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf))) + return; + + obj = (union acpi_object *)buf.pointer; + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) { + const union acpi_object *objs = obj->package.elements; + + *hcnt = (u16)objs[0].integer.value; + *lcnt = (u16)objs[1].integer.value; + *sda_hold = (u32)objs[2].integer.value; + } + + kfree(buf.pointer); +} + +static int phytium_i2c_acpi_configure(struct platform_device *pdev) +{ + struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); + struct i2c_timings *t = &dev->timings; + u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + const struct acpi_device_id *id; + + dev->adapter.nr = -1; + dev->tx_fifo_depth = 32; + dev->rx_fifo_depth = 32; + + /* + * Try to get SDA hold time and *CNT values from an ACPI method for + * selected speed modes. + */ + phytium_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, + &ss_ht); + phytium_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, + &fp_ht); + phytium_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, + &hs_ht); + phytium_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, + &fs_ht); + + switch (t->bus_freq_hz) { + case 100000: + dev->sda_hold_time = ss_ht; + break; + case 1000000: + dev->sda_hold_time = fp_ht; + break; + case 3400000: + dev->sda_hold_time = hs_ht; + break; + case 400000: + default: + dev->sda_hold_time = fs_ht; + break; + } + + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id && id->driver_data) + dev->flags |= (u32)id->driver_data; + + return 0; +} + +static const struct acpi_device_id phytium_i2c_acpi_match[] = { { "PHYT0038", + 0 }, + {} }; +MODULE_DEVICE_TABLE(acpi, phytium_i2c_acpi_match); +#else +static inline int phytium_i2c_acpi_configure(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + +static void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = + I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY | I2C_FUNC_SLAVE; + + dev->master_cfg = + IC_CON_MASTER | IC_CON_SLAVE_DISABLE | IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} + +static void i2c_phytium_configure_slave(struct phytium_i2c_dev *dev) +{ + dev->functionality = I2C_FUNC_SLAVE | IC_DEFAULT_FUNCTIONALITY; + + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | IC_CON_RESTART_EN | + IC_CON_STOP_DET_IFADDRESSED; + + dev->mode = PHYTIUM_IC_SLAVE; +} + +static int phytium_i2c_plat_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adap; + struct phytium_i2c_dev *dev; + struct i2c_timings *t; + u32 acpi_speed; + struct resource *mem; + int irq, ret, i; + static const int supported_speeds[] = { 0, 100000, 400000, 1000000, + 3400000 }; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct phytium_i2c_dev), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->i2c_resource_addr = mem->start; + dev->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + dev->dev = &pdev->dev; + dev->irq = irq; + dev->first_time_init_master = false; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->use_ipmb = false; + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_lock_init(&dev->i2c_lock); + platform_set_drvdata(pdev, dev); + + dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(dev->rst)) { + if (PTR_ERR(dev->rst) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + reset_control_deassert(dev->rst); + } + + t = &dev->timings; + i2c_parse_fw_timings(&pdev->dev, t, false); + + acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); + /* + * Some DSTDs use a non standard speed, round down to the lowest + * standard speed. + */ + for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) { + if (acpi_speed < supported_speeds[i]) + break; + } + acpi_speed = supported_speeds[i - 1]; + + /* + * Find bus speed from the "clock-frequency" device property, ACPI + * or by using fast mode if neither is set. + */ + if (acpi_speed && t->bus_freq_hz) + t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed); + else if (acpi_speed || t->bus_freq_hz) + t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); + else + t->bus_freq_hz = 400000; + + if (has_acpi_companion(&pdev->dev)) + phytium_i2c_acpi_configure(pdev); + + /* + * Only standard mode at 100kHz, fast mode at 400kHz, + * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. + */ + if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 && + t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) { + dev_err(&pdev->dev, + "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are " + "supported\n", + t->bus_freq_hz); + ret = -EINVAL; + goto exit_reset; + } + + if (i2c_detect_slave_mode(&pdev->dev)) { + i2c_phytium_configure_slave(dev); + } else { + dev->first_time_init_master = true; + i2c_phytium_configure_master(dev); + } + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (!i2c_phytium_prepare_clk(dev, true)) { + u64 clk_khz; + + dev->get_clk_rate_khz = i2c_phytium_get_clk_rate_khz; + clk_khz = dev->get_clk_rate_khz(dev); + + if (!dev->sda_hold_time && t->sda_hold_ns) + dev->sda_hold_time = div_u64( + clk_khz * t->sda_hold_ns + 500000, 1000000); + } + + dev->tx_fifo_depth = 7; + dev->rx_fifo_depth = 7; + dev->adapter.nr = pdev->id; + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); + adap->dev.of_node = pdev->dev.of_node; + + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | + DPM_FLAG_SMART_SUSPEND | + DPM_FLAG_LEAVE_SUSPENDED); + + /* The code below assumes runtime PM to be disabled. */ + WARN_ON(pm_runtime_enabled(&pdev->dev)); + + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + ret = i2c_phytium_probe(dev); + if (ret) + goto exit_probe; + + return ret; + +exit_probe: + pm_runtime_disable(dev->dev); +exit_reset: + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + return ret; +} + +static int phytium_i2c_plat_remove(struct platform_device *pdev) +{ + struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + + i2c_del_adapter(&dev->adapter); + + dev->disable(dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(dev->dev); + + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_i2c_of_match[] = { + { + .compatible = "phytium,i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_i2c_of_match); +#endif + +static int __maybe_unused phytium_i2c_plat_suspend(struct device *dev) +{ + struct phytium_i2c_dev *idev = dev_get_drvdata(dev); + + idev->disable(idev); + i2c_phytium_prepare_clk(idev, false); + + return 0; +} + +static int __maybe_unused phytium_i2c_plat_resume(struct device *dev) +{ + struct phytium_i2c_dev *idev = dev_get_drvdata(dev); + + i2c_phytium_prepare_clk(idev, true); + + idev->init(idev); + + return 0; +} + +static const struct dev_pm_ops phytium_i2c_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(phytium_i2c_plat_suspend, + phytium_i2c_plat_resume) + SET_RUNTIME_PM_OPS(phytium_i2c_plat_suspend, + phytium_i2c_plat_resume, NULL) +}; + +static struct platform_driver phytium_i2c_driver = { + .probe = phytium_i2c_plat_probe, + .remove = phytium_i2c_plat_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(phytium_i2c_of_match), + .acpi_match_table = ACPI_PTR(phytium_i2c_acpi_match), + .pm = &phytium_i2c_dev_pm_ops, + }, +}; +module_platform_driver(phytium_i2c_driver); + +MODULE_ALIAS("platform:i2c-phytium"); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-phytium-common.c b/drivers/i2c/busses/i2c-phytium-common.c index a35bab2ffa..1c890c2148 100644 --- a/drivers/i2c/busses/i2c-phytium-common.c +++ b/drivers/i2c/busses/i2c-phytium-common.c @@ -9,11 +9,12 @@ * * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. */ + #include #include -#include -#include #include +#include +#include #include #include #include @@ -22,36 +23,111 @@ #include #include "i2c-phytium-core.h" +extern int i2c_phytium_init_master(struct phytium_i2c_dev *dev); +extern int i2c_phytium_init_slave(struct phytium_i2c_dev *dev); + +struct i2c_pair { + u8 index; + u64 i2c_base_addr; +}; + +struct i2c_pair ipr[] = { + { 0, 0x28014000 }, { 1, 0x28016000 }, { 2, 0x28018000 }, + { 3, 0x2801a000 }, { 4, 0x2801c000 }, { 5, 0x2801e000 }, + { 6, 0x28020000 }, { 7, 0x28022000 }, { 8, 0x28024000 }, + { 9, 0x28026000 }, { 0xa, 0x28028000 }, { 0xb, 0x2802a000 }, + { 0xc, 0x2802c000 }, { 0xd, 0x2802e000 }, { 0xe, 0x28030000 }, + { 0xf, 0x28032000 }, +}; +/* + * Add for i2c recover when controller status is wrong + */ +int get_i2c_recover_index(u64 resource_addr) +{ + int index; + for (index = 0; index < sizeof(ipr) / sizeof(struct i2c_pair); index++) + if (resource_addr == ipr[index].i2c_base_addr) + return index; + return -1; +} + +int i2c_recover_controller(struct phytium_i2c_dev *dev) +{ + int index; + u32 reset_reg_val; + u32 reg_val; + unsigned long flags; + void __iomem *i2c_base; + + spin_lock_irqsave(&dev->i2c_lock, flags); + + index = get_i2c_recover_index(dev->i2c_resource_addr); + if (index < 0) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return -EFAULT; + } + i2c_base = ioremap(I2C_RESET_BASE_ADDR, REG_LEN); + if (!i2c_base) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return -EFAULT; + } + + reg_val = readl_relaxed(i2c_base + I2C_RESET_OFFSET); + reset_reg_val = reg_val; + reg_val &= ~(1 << (SHIFT_BIT + index)); + writel_relaxed(reg_val, i2c_base + I2C_RESET_OFFSET); + writel_relaxed(reset_reg_val, i2c_base + I2C_RESET_OFFSET); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; + dev->status = STATUS_IDLE; + if (dev->slave) + phytium_writel(dev, dev->slave->addr, IC_SAR); +#endif + iounmap(i2c_base); + i2c_base = NULL; + + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +int do_change_mode(int target_mode, struct phytium_i2c_dev *dev) +{ + if (target_mode == PHYTIUM_IC_MASTER) { + dev->disable_int(dev); + dev->disable(dev); + i2c_phytium_init_master(dev); + } else if (target_mode == PHYTIUM_IC_SLAVE) { + i2c_phytium_init_slave(dev); + __i2c_phytium_enable(dev); + } + + dev->mode = target_mode; + return 0; +} +#endif static char *abort_sources[] = { - [ABRT_7B_ADDR_NOACK] = - "slave address not acknowledged (7bit mode)", + [ABRT_7B_ADDR_NOACK] = "slave address not acknowledged (7bit mode)", [ABRT_10ADDR1_NOACK] = "first address byte not acknowledged (10bit mode)", [ABRT_10ADDR2_NOACK] = "second address byte not acknowledged (10bit mode)", - [ABRT_TXDATA_NOACK] = - "data not acknowledged", - [ABRT_GCALL_NOACK] = - "no acknowledgment for a general call", - [ABRT_GCALL_READ] = - "read after general call", - [ABRT_SBYTE_ACKDET] = - "start byte acknowledged", + [ABRT_TXDATA_NOACK] = "data not acknowledged", + [ABRT_GCALL_NOACK] = "no acknowledgement for a general call", + [ABRT_GCALL_READ] = "read after general call", + [ABRT_SBYTE_ACKDET] = "start byte acknowledged", [ABRT_SBYTE_NORSTRT] = "trying to send start byte when restart is disabled", [ABRT_10B_RD_NORSTRT] = "trying to read when restart is disabled (10bit mode)", - [ABRT_MASTER_DIS] = - "trying to use disabled adapter", - [ARB_LOST] = - "lost arbitration", + [ABRT_MASTER_DIS] = "trying to use disabled adapter", + [ARB_LOST] = "lost arbitration", [ABRT_SLAVE_FLUSH_TXFIFO] = "read command so flush old data in the TX FIFO", [ABRT_SLAVE_ARBLOST] = "slave lost the bus while transmitting data to a remote master", - [ABRT_SLAVE_RD_INTX] = - "incorrect slave-transmitter mode configuration", + [ABRT_SLAVE_RD_INTX] = "incorrect slave-transmitter mode configuration", }; u32 phytium_readl(struct phytium_i2c_dev *dev, int offset) @@ -69,7 +145,8 @@ u32 i2c_phytium_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) if (cond) return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset; else - return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + offset; + return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + + offset; } u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) @@ -96,22 +173,7 @@ int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev) void __i2c_phytium_disable(struct phytium_i2c_dev *dev) { - int timeout = 100; - - do { - __i2c_phytium_disable_nowait(dev); - if ((phytium_readl(dev, IC_ENABLE_STATUS) & 1) == 0) - return; - - /* - * Wait 10 times the signaling period of the highest I2C - * transfer supported by the driver (for 400KHz this is - * 25us). - */ - usleep_range(25, 250); - } while (timeout--); - - dev_warn(dev->dev, "timeout in disabling adapter\n"); + __i2c_phytium_disable_nowait(dev); } unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev) @@ -136,19 +198,10 @@ EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk); int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev) { - int timeout = 20; /* 20 ms */ - - while (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { - if (timeout <= 0) { - dev_warn(dev->dev, "timeout waiting for bus ready\n"); - i2c_recover_bus(&dev->adapter); - - if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) - return -ETIMEDOUT; - return 0; - } - timeout--; - usleep_range(1000, 1100); + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) + return -ETIMEDOUT; + return 0; } return 0; @@ -160,22 +213,19 @@ int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev) int i; if (abort_source & IC_TX_ABRT_NOACK) { - for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) - dev_dbg(dev->dev, - "%s: %s\n", __func__, abort_sources[i]); + for_each_set_bit (i, &abort_source, ARRAY_SIZE(abort_sources)); return -EREMOTEIO; } - for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) - dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + for_each_set_bit (i, &abort_source, ARRAY_SIZE(abort_sources)); - if (abort_source & IC_TX_ARB_LOST) + if (abort_source & IC_TX_ARB_LOST) { return -EAGAIN; - else if (abort_source & IC_TX_ABRT_GCALL_READ) + } else if (abort_source & IC_TX_ABRT_GCALL_READ) { return -EINVAL; - else + } else { return -EIO; - + } return 0; } diff --git a/drivers/i2c/busses/i2c-phytium-core.h b/drivers/i2c/busses/i2c-phytium-core.h index 2081a25272..16f14be198 100644 --- a/drivers/i2c/busses/i2c-phytium-core.h +++ b/drivers/i2c/busses/i2c-phytium-core.h @@ -10,225 +10,244 @@ * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. */ +#include #include #include -#include -#define IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ - I2C_FUNC_SMBUS_BYTE | \ - I2C_FUNC_SMBUS_BYTE_DATA | \ - I2C_FUNC_SMBUS_WORD_DATA | \ - I2C_FUNC_SMBUS_BLOCK_DATA | \ - I2C_FUNC_SMBUS_I2C_BLOCK) - -#define IC_CON_MASTER 0x1 -#define IC_CON_SPEED_STD 0x2 -#define IC_CON_SPEED_FAST 0x4 -#define IC_CON_SPEED_HIGH 0x6 -#define IC_CON_SPEED_MASK 0x6 -#define IC_CON_10BITADDR_SLAVE 0x8 -#define IC_CON_10BITADDR_MASTER 0x10 -#define IC_CON_RESTART_EN 0x20 -#define IC_CON_SLAVE_DISABLE 0x40 -#define IC_CON_STOP_DET_IFADDRESSED 0x80 -#define IC_CON_TX_EMPTY_CTRL 0x100 -#define IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200 - -#define IC_CON 0x0 -#define IC_TAR 0x4 -#define IC_SAR 0x8 -#define IC_DATA_CMD 0x10 -#define IC_SS_SCL_HCNT 0x14 -#define IC_SS_SCL_LCNT 0x18 -#define IC_FS_SCL_HCNT 0x1c -#define IC_FS_SCL_LCNT 0x20 -#define IC_HS_SCL_HCNT 0x24 -#define IC_HS_SCL_LCNT 0x28 -#define IC_INTR_STAT 0x2c -#define IC_INTR_MASK 0x30 -#define IC_RAW_INTR_STAT 0x34 -#define IC_RX_TL 0x38 -#define IC_TX_TL 0x3c -#define IC_CLR_INTR 0x40 -#define IC_CLR_RX_UNDER 0x44 -#define IC_CLR_RX_OVER 0x48 -#define IC_CLR_TX_OVER 0x4c -#define IC_CLR_RD_REQ 0x50 -#define IC_CLR_TX_ABRT 0x54 -#define IC_CLR_RX_DONE 0x58 -#define IC_CLR_ACTIVITY 0x5c -#define IC_CLR_STOP_DET 0x60 -#define IC_CLR_START_DET 0x64 -#define IC_CLR_GEN_CALL 0x68 -#define IC_ENABLE 0x6c -#define IC_STATUS 0x70 -#define IC_TXFLR 0x74 -#define IC_RXFLR 0x78 -#define IC_SDA_HOLD 0x7c -#define IC_TX_ABRT_SOURCE 0x80 -#define IC_ENABLE_STATUS 0x9c -#define IC_SMBCLK_LOW_MEXT 0xa8 -#define IC_SMBCLK_LOW_TIMEOUT 0xac -#define IC_SMBDAT_STUCK_TIMEOUT 0xb4 -#define IC_CLR_SMBCLK_EXT_LOW_TIMEOUT 0xbc -#define IC_CLR_SMBCLK_TMO_LOW_TIMEOUT 0xc0 -#define IC_CLR_SMBDAT_LOW_TIMEOUT 0xc4 -#define IC_CLR_SMBALERT_IN_N 0xd0 - -#define IC_INTR_RX_UNDER 0x001 -#define IC_INTR_RX_OVER 0x002 -#define IC_INTR_RX_FULL 0x004 -#define IC_INTR_TX_OVER 0x008 -#define IC_INTR_TX_EMPTY 0x010 -#define IC_INTR_RD_REQ 0x020 -#define IC_INTR_TX_ABRT 0x040 -#define IC_INTR_RX_DONE 0x080 -#define IC_INTR_ACTIVITY 0x100 -#define IC_INTR_STOP_DET 0x200 -#define IC_INTR_START_DET 0x400 -#define IC_INTR_GEN_CALL 0x800 -#define IC_INTR_SMBCLK_EXT_LOW_TIMEOUT 0x1000 -#define IC_INTR_SMBCLK_TMO_LOW_TIMEOUT 0x2000 -#define IC_INTR_SMBSDA_LOW_TIMEOUT 0x4000 -#define IC_INTR_SMBALERT_IN_N 0x20000 - -#define IC_INTR_DEFAULT_MASK (IC_INTR_RX_FULL | \ - IC_INTR_TX_ABRT | \ - IC_INTR_STOP_DET) -#define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | \ - IC_INTR_TX_EMPTY) -#define IC_INTR_SLAVE_MASK (IC_INTR_DEFAULT_MASK | \ - IC_INTR_RX_DONE | \ - IC_INTR_RX_UNDER | \ - IC_INTR_RD_REQ) -#define IC_INTR_SMBUS_MASK (IC_INTR_MASTER_MASK | \ - IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ - IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | \ - IC_INTR_SMBSDA_LOW_TIMEOUT) - -#define IC_STATUS_ACTIVITY 0x1 -#define IC_STATUS_TFE BIT(2) -#define IC_STATUS_MASTER_ACTIVITY BIT(5) -#define IC_STATUS_SLAVE_ACTIVITY BIT(6) - -#define IC_SDA_HOLD_RX_SHIFT 16 -#define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) - -#define IC_ERR_TX_ABRT 0x1 - -#define IC_TAR_10BITADDR_MASTER BIT(12) - -#define IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) -#define IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) - -#define STATUS_IDLE 0x0 -#define STATUS_WRITE_IN_PROGRESS 0x1 -#define STATUS_READ_IN_PROGRESS 0x2 +#include + +#define IC_DEFAULT_FUNCTIONALITY \ + (I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK) + +#define IC_CON_MASTER 0x1 +#define IC_CON_SPEED_STD 0x2 +#define IC_CON_SPEED_FAST 0x4 +#define IC_CON_SPEED_HIGH 0x6 +#define IC_CON_SPEED_MASK 0x6 +#define IC_CON_10BITADDR_SLAVE 0x8 +#define IC_CON_10BITADDR_MASTER 0x10 +#define IC_CON_RESTART_EN 0x20 +#define IC_CON_SLAVE_DISABLE 0x40 +#define IC_CON_STOP_DET_IFADDRESSED 0x80 +#define IC_CON_TX_EMPTY_CTRL 0x100 +#define IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200 + +#define IC_CON 0x0 +#define IC_TAR 0x4 +#define IC_SAR 0x8 +#define IC_DATA_CMD 0x10 +#define IC_SS_SCL_HCNT 0x14 +#define IC_SS_SCL_LCNT 0x18 +#define IC_FS_SCL_HCNT 0x1c +#define IC_FS_SCL_LCNT 0x20 +#define IC_HS_SCL_HCNT 0x24 +#define IC_HS_SCL_LCNT 0x28 +#define IC_INTR_STAT 0x2c +#define IC_INTR_MASK 0x30 +#define IC_RAW_INTR_STAT 0x34 +#define IC_RX_TL 0x38 +#define IC_TX_TL 0x3c +#define IC_CLR_INTR 0x40 +#define IC_CLR_RX_UNDER 0x44 +#define IC_CLR_RX_OVER 0x48 +#define IC_CLR_TX_OVER 0x4c +#define IC_CLR_RD_REQ 0x50 +#define IC_CLR_TX_ABRT 0x54 +#define IC_CLR_RX_DONE 0x58 +#define IC_CLR_ACTIVITY 0x5c +#define IC_CLR_STOP_DET 0x60 +#define IC_CLR_START_DET 0x64 +#define IC_CLR_GEN_CALL 0x68 +#define IC_ENABLE 0x6c +#define IC_STATUS 0x70 +#define IC_TXFLR 0x74 +#define IC_RXFLR 0x78 +#define IC_SDA_HOLD 0x7c +#define IC_TX_ABRT_SOURCE 0x80 +#define IC_ENABLE_STATUS 0x9c +#define IC_SMBCLK_LOW_MEXT 0xa8 +#define IC_SMBCLK_LOW_TIMEOUT 0xac +#define IC_SMBDAT_STUCK_TIMEOUT 0xb4 +#define IC_CLR_SMBCLK_EXT_LOW_TIMEOUT 0xbc +#define IC_CLR_SMBCLK_TMO_LOW_TIMEOUT 0xc0 +#define IC_CLR_SMBDAT_LOW_TIMEOUT 0xc4 +#define IC_CLR_SMBALERT_IN_N 0xd0 + +#define IC_INTR_RX_UNDER 0x001 +#define IC_INTR_RX_OVER 0x002 +#define IC_INTR_RX_FULL 0x004 +#define IC_INTR_TX_OVER 0x008 +#define IC_INTR_TX_EMPTY 0x010 +#define IC_INTR_RD_REQ 0x020 +#define IC_INTR_TX_ABRT 0x040 +#define IC_INTR_RX_DONE 0x080 +#define IC_INTR_ACTIVITY 0x100 +#define IC_INTR_STOP_DET 0x200 +#define IC_INTR_START_DET 0x400 +#define IC_INTR_GEN_CALL 0x800 +#define IC_INTR_SMBCLK_EXT_LOW_TIMEOUT 0x1000 +#define IC_INTR_SMBCLK_TMO_LOW_TIMEOUT 0x2000 +#define IC_INTR_SMBSDA_LOW_TIMEOUT 0x4000 +#define IC_INTR_SMBALERT_IN_N 0x20000 + +#define IC_INTR_DEFAULT_MASK \ + (IC_INTR_RX_FULL | IC_INTR_TX_ABRT | IC_INTR_STOP_DET) +#define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | IC_INTR_TX_EMPTY) +#define IC_INTR_SLAVE_MASK \ + (IC_INTR_DEFAULT_MASK | IC_INTR_RX_DONE | IC_INTR_RX_UNDER | \ + IC_INTR_RD_REQ) +#define IC_INTR_SMBUS_MASK \ + (IC_INTR_MASTER_MASK | IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ + IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | IC_INTR_SMBSDA_LOW_TIMEOUT) + +#define IC_STATUS_ACTIVITY 0x1 +#define IC_STATUS_TFE BIT(2) +#define IC_STATUS_MASTER_ACTIVITY BIT(5) +#define IC_STATUS_SLAVE_ACTIVITY BIT(6) + +#define IC_SDA_HOLD_RX_SHIFT 16 +#define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) + +#define IC_ERR_TX_ABRT 0x1 + +#define IC_TAR_10BITADDR_MASTER BIT(12) + +#define IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) +#define IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) + +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 /* * operation modes */ -#define PHYTIUM_IC_MASTER 0 -#define PHYTIUM_IC_SLAVE 1 - -#define ABRT_7B_ADDR_NOACK 0 -#define ABRT_10ADDR1_NOACK 1 -#define ABRT_10ADDR2_NOACK 2 -#define ABRT_TXDATA_NOACK 3 -#define ABRT_GCALL_NOACK 4 -#define ABRT_GCALL_READ 5 -#define ABRT_SBYTE_ACKDET 7 -#define ABRT_SBYTE_NORSTRT 9 -#define ABRT_10B_RD_NORSTRT 10 -#define ABRT_MASTER_DIS 11 -#define ARB_LOST 12 -#define ABRT_SLAVE_FLUSH_TXFIFO 13 -#define ABRT_SLAVE_ARBLOST 14 -#define ABRT_SLAVE_RD_INTX 15 - -#define IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) -#define IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) -#define IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) -#define IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) -#define IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) -#define IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) -#define IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) -#define IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) -#define IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) -#define IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) -#define IC_TX_ARB_LOST (1UL << ARB_LOST) -#define IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX) -#define IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST) -#define IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO) - -#define IC_TX_ABRT_NOACK (IC_TX_ABRT_7B_ADDR_NOACK | \ - IC_TX_ABRT_10ADDR1_NOACK | \ - IC_TX_ABRT_10ADDR2_NOACK | \ - IC_TX_ABRT_TXDATA_NOACK | \ - IC_TX_ABRT_GCALL_NOACK) -#define CONTROLLER_TYPE_IIC 0 -#define CONTROLLER_TYPE_SMBUS 1 +#define PHYTIUM_IC_MASTER 0 +#define PHYTIUM_IC_SLAVE 1 +#if IS_ENABLED(CONFIG_I2C_SLAVE) +#define IPMB_MIX_LEN 6 +enum phytium_i2c_slave_state { + SLAVE_STATE_IDLE, + SLAVE_STATE_RECV, + SLAVE_STATE_SEND, + SLAVE_STATE_REQUEST, + SLAVE_STATE_RESPONSE +}; +#endif +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 +#define ABRT_SLAVE_FLUSH_TXFIFO 13 +#define ABRT_SLAVE_ARBLOST 14 +#define ABRT_SLAVE_RD_INTX 15 + +#define IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define IC_TX_ARB_LOST (1UL << ARB_LOST) +#define IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX) +#define IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST) +#define IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO) + +#define IC_TX_ABRT_NOACK \ + (IC_TX_ABRT_7B_ADDR_NOACK | IC_TX_ABRT_10ADDR1_NOACK | \ + IC_TX_ABRT_10ADDR2_NOACK | IC_TX_ABRT_TXDATA_NOACK | \ + IC_TX_ABRT_GCALL_NOACK) +#define CONTROLLER_TYPE_IIC 0 +#define CONTROLLER_TYPE_SMBUS 1 struct phytium_i2c_dev { - struct device *dev; - void __iomem *base; - int irq; - u32 flags; - struct completion cmd_complete; - struct clk *clk; - struct reset_control *rst; - int mode; - struct i2c_client *slave; - u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); - - struct i2c_adapter adapter; - struct i2c_client *ara; + struct device *dev; + void __iomem *base; + int irq; + u32 flags; + struct completion cmd_complete; + struct clk *clk; + struct reset_control *rst; + int mode; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + bool use_ipmb; + enum phytium_i2c_slave_state slave_state; +#endif + spinlock_t i2c_lock; + struct i2c_client *slave; + u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); + + struct i2c_adapter adapter; + struct i2c_client *ara; struct i2c_smbus_alert_setup alert_data; struct phytium_pci_i2c *controller; - unsigned int status; - int cmd_err; - u32 abort_source; - - struct i2c_msg *msgs; - int msgs_num; - int msg_write_idx; - int msg_read_idx; - int msg_err; - u32 tx_buf_len; - u8 *tx_buf; - u32 rx_buf_len; - u8 *rx_buf; - - u32 master_cfg; - u32 slave_cfg; - u32 functionality; - unsigned int tx_fifo_depth; - unsigned int rx_fifo_depth; - int rx_outstanding; - - struct i2c_timings timings; - u32 sda_hold_time; - u16 ss_hcnt; - u16 ss_lcnt; - u16 fs_hcnt; - u16 fs_lcnt; - u16 fp_hcnt; - u16 fp_lcnt; - u16 hs_hcnt; - u16 hs_lcnt; - - bool pm_disabled; - void (*disable)(struct phytium_i2c_dev *dev); - void (*disable_int)(struct phytium_i2c_dev *dev); - int (*init)(struct phytium_i2c_dev *dev); + unsigned int status; + int cmd_err; + u32 abort_source; + + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + int msg_read_idx; + int msg_err; + u32 tx_buf_len; + u8 *tx_buf; + u32 rx_buf_len; + u8 *rx_buf; + + u32 master_cfg; + u32 slave_cfg; + u32 functionality; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; + int rx_outstanding; + + struct i2c_timings timings; + u32 sda_hold_time; + u16 ss_hcnt; + u16 ss_lcnt; + u16 fs_hcnt; + u16 fs_lcnt; + u16 fp_hcnt; + u16 fp_lcnt; + u16 hs_hcnt; + u16 hs_lcnt; + u64 i2c_resource_addr; + bool pm_disabled; + bool first_time_init_master; + void (*disable)(struct phytium_i2c_dev *dev); + void (*disable_int)(struct phytium_i2c_dev *dev); + int (*init)(struct phytium_i2c_dev *dev); }; -#define ACCESS_INTR_MASK 0x00000004 +#define ACCESS_INTR_MASK 0x00000004 + +#define DEFAULT_CLOCK_FREQUENCY 48000000 -#define DEFAULT_CLOCK_FREQUENCY 48000000 +#define I2C_RESET_BASE_ADDR 0x2807e000 +#define I2C_RESET_OFFSET 0x4 +#define REG_LEN 0x100 +#define SHIFT_BIT 16 +int i2c_recover_controller(struct phytium_i2c_dev *dev); +#if IS_ENABLED(CONFIG_I2C_SLAVE) +int do_change_mode(int target_mode, struct phytium_i2c_dev *dev); +#endif u32 phytium_readl(struct phytium_i2c_dev *dev, int offset); void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset); unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-phytium-mix.c b/drivers/i2c/busses/i2c-phytium-mix.c new file mode 100644 index 0000000000..4bc3c18366 --- /dev/null +++ b/drivers/i2c/busses/i2c-phytium-mix.c @@ -0,0 +1,891 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2C adapter driver. + * + * Derived from Synopysys I2C driver. + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd.:w + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +static int i2c_phytium_unreg_slave(struct i2c_client *slave); +static int i2c_phytium_reg_slave(struct i2c_client *slave); + +static void i2c_phytium_configure_fifo_slave(struct phytium_i2c_dev *dev) +{ + /* Configure Tx/Rx FIFO threshold levels. */ + phytium_writel(dev, 0, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + dev->mode = PHYTIUM_IC_SLAVE; + /* Configure the I2C slave. */ + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | IC_CON_RESTART_EN | + IC_CON_STOP_DET_IFADDRESSED; + phytium_writel(dev, dev->slave_cfg, IC_CON); + phytium_writel(dev, IC_INTR_SLAVE_MASK, IC_INTR_MASK); +} + +int i2c_phytium_init_slave(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter. */ + __i2c_phytium_disable(dev); + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + i2c_phytium_configure_fifo_slave(dev); + + return 0; +} + +static void i2c_phytium_configure_master_ex(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY; + + dev->master_cfg = + IC_CON_MASTER | IC_CON_SLAVE_DISABLE | IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} +int i2c_phytium_init_master(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter */ + __i2c_phytium_disable(dev); + + /* Write standard speed timing parameters */ + phytium_writel(dev, dev->ss_hcnt, IC_SS_SCL_HCNT); + phytium_writel(dev, dev->ss_lcnt, IC_SS_SCL_LCNT); + + /* Write fast mode/fast mode plus timing parameters */ + phytium_writel(dev, dev->fs_hcnt, IC_FS_SCL_HCNT); + phytium_writel(dev, dev->fs_lcnt, IC_FS_SCL_LCNT); + + /* Write high speed timing parameters if supported */ + if (dev->hs_hcnt && dev->hs_hcnt) { + phytium_writel(dev, dev->hs_hcnt, IC_HS_SCL_HCNT); + phytium_writel(dev, dev->hs_lcnt, IC_HS_SCL_LCNT); + } + dev->mode = PHYTIUM_IC_MASTER; + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + /* Configure Tx/Rx FIFO threshold levels */ + phytium_writel(dev, dev->tx_fifo_depth >> 1, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + + /* Configure the I2C master */ + if (dev->first_time_init_master == false) { + i2c_phytium_configure_master_ex(dev); + dev->first_time_init_master = true; + } + phytium_writel(dev, dev->master_cfg, IC_CON); + + return 0; +} + +static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con, ic_tar = 0; + + /* Disable the adapter */ + __i2c_phytium_disable(dev); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_state == SLAVE_STATE_IDLE) + dev->slave_state = SLAVE_STATE_REQUEST; +#endif + + /* If the slave address is 10-bit address, enable 10BITADDR */ + ic_con = phytium_readl(dev, IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { + ic_con |= IC_CON_10BITADDR_MASTER; + ic_tar = IC_TAR_10BITADDR_MASTER; + } else { + ic_con &= ~IC_CON_10BITADDR_MASTER; + } + + phytium_writel(dev, ic_con, IC_CON); + + /* + * Set the slave (target) address and enable 10-bit addressing mode + * if applicable. + */ + phytium_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, IC_TAR); + + /* Enforce disabled interrupts */ + i2c_phytium_disable_int(dev); + + /* Enable the adapter */ + __i2c_phytium_enable(dev); + + /* Dummy read */ + phytium_readl(dev, IC_ENABLE_STATUS); + + /* Clear and enable interrupts */ + phytium_readl(dev, IC_CLR_INTR); + phytium_writel(dev, IC_INTR_SMBUS_MASK, IC_INTR_MASK); +} + +static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 intr_mask; + int tx_limit, rx_limit; + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf; + bool need_restart = false; + + intr_mask = IC_INTR_MASTER_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + u32 flags = msgs[dev->msg_write_idx].flags; + + if (msgs[dev->msg_write_idx].addr != addr) { + dev->msg_err = -EINVAL; + break; + } + + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_write_idx].len = 1 + I2C_SMBUS_BLOCK_MAX; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + if ((dev->master_cfg & IC_CON_RESTART_EN) && + (dev->msg_write_idx > 0)) + need_restart = true; + } + + tx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_TXFLR); + rx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_RXFLR); + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + u32 cmd = 0; + + if (dev->msg_write_idx == dev->msgs_num - 1 && buf_len == 1) { + cmd |= BIT(9); + } + if (need_restart) { + cmd |= BIT(10); + need_restart = false; + } + + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + /* avoid rx buffer overrun */ + if (dev->rx_outstanding >= dev->rx_fifo_depth) + break; + + phytium_writel(dev, cmd | 0x100, IC_DATA_CMD); + rx_limit--; + dev->rx_outstanding++; + } else { + phytium_writel(dev, cmd | *buf++, IC_DATA_CMD); + } + tx_limit--; + buf_len--; + } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + /* + * Because we don't know the buffer length in the + * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop + * the transaction here. + */ + if (buf_len > 0) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } else { + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + } + + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; + + phytium_writel(dev, intr_mask, IC_INTR_MASK); +} + +static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) +{ + struct i2c_msg *msgs = dev->msgs; + u32 flags = msgs[dev->msg_read_idx].flags; + + /* + * Adjust the buffer length and mask the flag + * after receiving the first byte. + */ + if (len > I2C_SMBUS_BLOCK_MAX) { + len = 0; + } + + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; + dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); + + /* require adding one byte to trigger the i2c_phytium_xfer finishing the transaction. */ + if (dev->tx_buf_len == 0) { + dev->tx_buf_len = 1; + len = dev->rx_outstanding + 1; + } + + return len; +} + +static void i2c_phytium_read(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 len; + u8 *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + rx_valid = phytium_readl(dev, IC_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) { + u32 flags = msgs[dev->msg_read_idx].flags; + + *buf = phytium_readl(dev, IC_DATA_CMD); + /* Ensure length byte is a valid value */ + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + len = i2c_phytium_recv_len(dev, *buf); + if (*buf > I2C_SMBUS_BLOCK_MAX || *buf == 0) { + dev_err(dev->dev, "The value %d is out of the SMBus range [1~32]\n", *buf); + *buf = 0; + dev->msg_err = -EINVAL; + } + } + buf++; + dev->rx_outstanding--; + } + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } else + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], + int num) +{ + struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter); + int ret; + unsigned long flags; + bool change_mode = false; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) { + dev_err(dev->dev, "pm runtime get sync err.\n"); + goto pm_exit; + } + + spin_lock_irqsave(&dev->i2c_lock, flags); + reinit_completion(&dev->cmd_complete); + + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_state != SLAVE_STATE_RESPONSE && + dev->slave_state != SLAVE_STATE_IDLE) { + ret = -EAGAIN; + goto done; + } +#endif + ret = i2c_phytium_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->mode == PHYTIUM_IC_SLAVE) { + if (do_change_mode(PHYTIUM_IC_MASTER, dev) == 0) + change_mode = true; + } +#endif + + /* Start the transfers */ + i2c_phytium_xfer_init(dev); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + /* Wait for tx to complete */ + if (!wait_for_completion_timeout(&dev->cmd_complete, + adapter->timeout)) { + dev_err(dev->dev, "controller timed out\n"); + i2c_recover_controller(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); + ret = -ETIMEDOUT; + goto done; + } + spin_lock_irqsave(&dev->i2c_lock, flags); + __i2c_phytium_disable_nowait(dev); + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + if (likely(!dev->cmd_err && !dev->status)) { + ret = num; + goto done; + } + + /* We have got an error */ + if (dev->cmd_err == IC_ERR_TX_ABRT) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + ret = i2c_phytium_handle_tx_abort(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); + goto done; + } + + ret = -EIO; + +done: + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (change_mode == true) { + do_change_mode(PHYTIUM_IC_SLAVE, dev); + } + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_unlock_irqrestore(&dev->i2c_lock, flags); + +pm_exit: + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static int i2c_phytium_reg_slave(struct i2c_client *slave) +{ + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + spin_lock_irqsave(&dev->i2c_lock, flags); + + if (dev->slave) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return -EBUSY; + } + if (slave->flags & I2C_CLIENT_TEN) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return -EAFNOSUPPORT; + } + pm_runtime_get_sync(dev->dev); + + /* + * Set slave address in the IC_SAR register, + * the address to which the i2c responds. + */ + __i2c_phytium_disable_nowait(dev); + i2c_phytium_init_slave(dev); + phytium_writel(dev, slave->addr, IC_SAR); + dev->slave = slave; + dev->mode = PHYTIUM_IC_SLAVE; + __i2c_phytium_enable(dev); + + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + dev->use_ipmb = true; + + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} + +static int i2c_phytium_unreg_slave(struct i2c_client *slave) +{ + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + spin_lock_irqsave(&dev->i2c_lock, flags); + dev->disable_int(dev); + dev->disable(dev); + dev->slave = NULL; + pm_runtime_put(dev->dev); + + dev->mode = PHYTIUM_IC_MASTER; + i2c_phytium_init_master(dev); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} +#endif + +static const struct i2c_algorithm i2c_phytium_algo = { + .master_xfer = i2c_phytium_xfer, + .functionality = i2c_phytium_func, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = i2c_phytium_reg_slave, + .unreg_slave = i2c_phytium_unreg_slave, +#endif +}; + +static const struct i2c_adapter_quirks i2c_phytium_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) +{ + u32 stat; + + stat = phytium_readl(dev, IC_INTR_STAT); + if (stat & IC_INTR_RX_UNDER) + phytium_readl(dev, IC_CLR_RX_UNDER); + if (stat & IC_INTR_RX_OVER) + phytium_readl(dev, IC_CLR_RX_OVER); + if (stat & IC_INTR_TX_OVER) + phytium_readl(dev, IC_CLR_TX_OVER); + if (stat & IC_INTR_RD_REQ) + phytium_readl(dev, IC_CLR_RD_REQ); + if (stat & IC_INTR_TX_ABRT) { + dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); + phytium_readl(dev, IC_CLR_TX_ABRT); + } + if (stat & IC_INTR_RX_DONE) + phytium_readl(dev, IC_CLR_RX_DONE); + if (stat & IC_INTR_ACTIVITY) + phytium_readl(dev, IC_CLR_ACTIVITY); + if (stat & IC_INTR_STOP_DET) + phytium_readl(dev, IC_CLR_STOP_DET); + if (stat & IC_INTR_START_DET) + phytium_readl(dev, IC_CLR_START_DET); + if (stat & IC_INTR_GEN_CALL) + phytium_readl(dev, IC_CLR_GEN_CALL); + if (stat & IC_INTR_SMBCLK_EXT_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBCLK_EXT_LOW_TIMEOUT); + if (stat & IC_INTR_SMBCLK_TMO_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBCLK_TMO_LOW_TIMEOUT); + if (stat & IC_INTR_SMBSDA_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBDAT_LOW_TIMEOUT); + if (stat & IC_INTR_SMBALERT_IN_N) + phytium_readl(dev, IC_CLR_SMBALERT_IN_N); + + return stat; +} + +static u32 i2c_phytium_read_clear_intrbits_slave(struct phytium_i2c_dev *dev) +{ + u32 stat; + + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = phytium_readl(IC_INTR_STAT); + * equals to, + * stat = phytium_readl(IC_RAW_INTR_STAT) & phytium_readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = phytium_readl(dev, IC_INTR_STAT); + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * phytium_readl(IC_INTR_STAT) to phytium_readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & IC_INTR_TX_ABRT) + phytium_readl(dev, IC_CLR_TX_ABRT); + if (stat & IC_INTR_RX_UNDER) + phytium_readl(dev, IC_CLR_RX_UNDER); + if (stat & IC_INTR_RX_OVER) + phytium_readl(dev, IC_CLR_RX_OVER); + if (stat & IC_INTR_TX_OVER) + phytium_readl(dev, IC_CLR_TX_OVER); + if (stat & IC_INTR_RX_DONE) + phytium_readl(dev, IC_CLR_RX_DONE); + if (stat & IC_INTR_ACTIVITY) + phytium_readl(dev, IC_CLR_ACTIVITY); + if (stat & IC_INTR_STOP_DET) + phytium_readl(dev, IC_CLR_STOP_DET); + if (stat & IC_INTR_START_DET) + phytium_readl(dev, IC_CLR_START_DET); + if (stat & IC_INTR_GEN_CALL) + phytium_readl(dev, IC_CLR_GEN_CALL); + + return stat; +} + +static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) +{ + u32 stat; + + stat = i2c_phytium_read_clear_intrbits(dev); + + /* SMBus interrupt */ + if (stat & + (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) & (~BIT(6)), + IC_ENABLE); + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(4), + IC_ENABLE); + goto abort; + } + + if (stat & IC_INTR_SMBSDA_LOW_TIMEOUT) { + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(6), + IC_ENABLE); + goto abort; + } + + if (stat & IC_INTR_SMBALERT_IN_N && dev->ara) + i2c_handle_smbus_alert(dev->ara); + + if (stat & IC_INTR_TX_ABRT) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* + * Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + phytium_writel(dev, 0, IC_INTR_MASK); + goto abort; + } + + if (stat & IC_INTR_RX_FULL) + i2c_phytium_read(dev); + + if (stat & IC_INTR_TX_EMPTY) + i2c_phytium_xfer_msg(dev); + +abort: + if ((stat & IC_INTR_TX_ABRT) || dev->msg_err){ + complete(&dev->cmd_complete); + } + else if (stat & IC_INTR_STOP_DET){ + if (dev->use_ipmb && dev->msgs->len >= IPMB_MIX_LEN && + dev->slave_state == SLAVE_STATE_REQUEST) + do_change_mode(PHYTIUM_IC_SLAVE, dev); + else + complete(&dev->cmd_complete); + } + else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { + /* Workaround to trigger pending interrupt */ + stat = phytium_readl(dev, IC_INTR_MASK); + i2c_phytium_disable_int(dev); + phytium_writel(dev, stat, IC_INTR_MASK); + } + + return 0; +} + +static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) +{ + u32 raw_stat, stat, enabled; + u8 val, slave_activity; + + enabled = phytium_readl(dev, IC_ENABLE); + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); + slave_activity = + ((phytium_readl(dev, IC_STATUS) & IC_STATUS_SLAVE_ACTIVITY) >> + 6); + + if (!enabled || !(raw_stat & ~IC_INTR_ACTIVITY) || !dev->slave) { + return 0; + } + stat = i2c_phytium_read_clear_intrbits_slave(dev); + + if (stat & IC_INTR_RX_FULL) { + if (dev->status != STATUS_WRITE_IN_PROGRESS) { + dev->status = STATUS_WRITE_IN_PROGRESS; + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, + &val); + dev->slave_state = SLAVE_STATE_RECV; + } + do { + val = phytium_readl(dev, IC_DATA_CMD); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, + &val); + val = phytium_readl(dev, IC_STATUS); + } while (val & BIT(3)); + } + + if (stat & IC_INTR_RD_REQ) { + if (slave_activity) { + phytium_readl(dev, IC_CLR_RD_REQ); + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, &val); + dev->status |= STATUS_READ_IN_PROGRESS; + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + dev->slave_state = SLAVE_STATE_SEND; + + } else { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_PROCESSED, &val); + } + phytium_writel(dev, val, IC_DATA_CMD); + } + } + + if (stat & IC_INTR_STOP_DET) { + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_state == SLAVE_STATE_RECV || + dev->slave_state == SLAVE_STATE_SEND) { + dev->status = STATUS_IDLE; + dev->slave_state = SLAVE_STATE_RESPONSE; + complete(&dev->cmd_complete); + } +#endif + } + + return 1; +} + +static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +{ + struct phytium_i2c_dev *dev = dev_id; + u32 stat, enabled; + spin_lock(&dev->i2c_lock); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->mode == PHYTIUM_IC_SLAVE) { + i2c_phytium_irq_handler_slave(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; + } +#endif + enabled = phytium_readl(dev, IC_ENABLE); + stat = phytium_readl(dev, IC_RAW_INTR_STAT); + if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) { + spin_unlock(&dev->i2c_lock); + return IRQ_NONE; + } + + i2c_phytium_irq_handler_master(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; +} + +static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) +{ + const char *mode_str, *fp_str = ""; + u32 sda_falling_time, scl_falling_time; + struct i2c_timings *t = &dev->timings; + u32 ic_clk; + int ret; + + /* Set standard and fast speed dividers for high/low periods */ + sda_falling_time = t->sda_fall_ns ?: 300; /* ns */ + scl_falling_time = t->scl_fall_ns ?: 300; /* ns */ + + /* Calculate SCL timing parameters for standard mode if not set */ + if (!dev->ss_hcnt || !dev->ss_lcnt) { + ic_clk = i2c_phytium_clk_rate(dev); + dev->ss_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->ss_lcnt = + i2c_phytium_scl_lcnt(ic_clk, 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", dev->ss_hcnt, + dev->ss_lcnt); + /* + * Set SCL timing parameters for fast mode or fast mode plus. Only + * difference is the timing parameter values since the registers + * are the same. + */ + if (t->bus_freq_hz == 1000000) { + /* + * Check are fast mode plus parameters available and use + * fast mode if not. + */ + if (dev->fp_hcnt && dev->fp_lcnt) { + dev->fs_hcnt = dev->fp_hcnt; + dev->fs_lcnt = dev->fp_lcnt; + fp_str = " Plus"; + } + } + /* + * Calculate SCL timing parameters for fast mode if not set. They are + * needed also in high speed mode. + */ + if (!dev->fs_hcnt || !dev->fs_lcnt) { + ic_clk = i2c_phytium_clk_rate(dev); + dev->fs_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->fs_lcnt = + i2c_phytium_scl_lcnt(ic_clk, 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", fp_str, + dev->fs_hcnt, dev->fs_lcnt); + + if (dev->hs_hcnt && dev->hs_lcnt) + dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", + dev->hs_hcnt, dev->hs_lcnt); + + ret = i2c_phytium_set_sda_hold(dev); + if (ret) + goto out; + + switch (dev->master_cfg & IC_CON_SPEED_MASK) { + case IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; + } + dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + +out: + return ret; +} + +int i2c_phytium_probe(struct phytium_i2c_dev *dev) +{ + struct i2c_adapter *adapter = &dev->adapter; + unsigned long irq_flags; + int ret; + const char *mode_str; + + init_completion(&dev->cmd_complete); + if (dev->mode == PHYTIUM_IC_MASTER) + dev->init = i2c_phytium_init_master; + else if (dev->mode == PHYTIUM_IC_SLAVE) + dev->init = i2c_phytium_init_slave; + + dev->disable = i2c_phytium_disable; + dev->disable_int = i2c_phytium_disable_int; + + ret = i2c_phytium_set_timings_master(dev); + if (ret) + return ret; + + ret = dev->init(dev); + if (ret) + return ret; + if (dev->mode == PHYTIUM_IC_MASTER) { + switch (dev->master_cfg & IC_CON_SPEED_MASK) { + case IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; + } + dev_err(dev->dev, "Bus speed: %s\n", mode_str); +/* XXX: should be initialized in firmware, remove it in future */ +#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + } + + snprintf(adapter->name, sizeof(adapter->name), "Phytium I2C adapter"); + adapter->retries = 3; + adapter->algo = &i2c_phytium_algo; + adapter->quirks = &i2c_phytium_quirks; + adapter->dev.parent = dev->dev; + i2c_set_adapdata(adapter, dev); + + irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; + + i2c_phytium_disable_int(dev); + ret = devm_request_irq(dev->dev, dev->irq, i2c_phytium_isr, irq_flags, + dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, + ret); + return ret; + } + + /* + * Increment PM usage count during adapter registration in order to + * avoid possible spurious runtime suspend when adapter device is + * registered to the device core and immediate resume in case bus has + * registered I2C slaves that do I2C transfers in their probe. + */ + pm_runtime_get_noresume(dev->dev); + ret = i2c_add_numbered_adapter(adapter); + if (ret) + dev_err(dev->dev, "fail to add adapter: %d\n", ret); + pm_runtime_put_noidle(dev->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_phytium_probe); + +MODULE_DESCRIPTION("Phytium I2C bus controller adapter"); +MODULE_LICENSE("GPL"); -- Gitee From 7f3921af1695dc9819581e9c65215484e6e5071a Mon Sep 17 00:00:00 2001 From: wangzc Date: Thu, 6 Jun 2024 13:57:35 +0800 Subject: [PATCH 05/41] gpio-phytium-core:Fix clutter signals in output mode In the initial state, when we need to set the GPIO output high level, we first set the direction, and then set the high level output. The output level will have a jump from low to high, instead of directly outputting high levels. Signed-off-by: wangzc --- drivers/gpio/gpio-phytium-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index d73298ea42..7f26de002e 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -128,14 +128,14 @@ int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, return -EINVAL; ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + phytium_gpio_set(gc, offset, value); + raw_spin_lock_irqsave(&gpio->lock, flags); writel(readl(ddr) | BIT(loc.offset), ddr); raw_spin_unlock_irqrestore(&gpio->lock, flags); - phytium_gpio_set(gc, offset, value); - return 0; } EXPORT_SYMBOL_GPL(phytium_gpio_direction_output); -- Gitee From 25308da0a5c4782b7a46fed5e1b0c3154e58dade Mon Sep 17 00:00:00 2001 From: hujun Date: Mon, 4 Dec 2023 11:34:57 +0800 Subject: [PATCH 06/41] phytium-quadqspi:add quadpage program support qspi quada page program: &qspi0 { status = "okay"; flash@0 { label = "bmc"; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; #include "openbmc-pe2201-flash-layout.dtsi" }; }; spi-nor flash_info add SPI_NOR_QUAD_WRITE: { "gd25b512me", INFO(0xc8471a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, } qspi clk conf in dts described as: &qspi0 { status = "okay"; flash@0 { ... spi-clk-div = <3>; ... }; }; After each data write is completed, clear 0 to wr_cfg and cmd_port register. Add QSPI indirect write mode. Signed-off-by: hujun Signed-off-by: wangzc --- drivers/mtd/spi-nor/phytium-quadspi.c | 215 ++++++++++++++++++-------- drivers/mtd/spi-nor/spi-nor.c | 28 +++- 2 files changed, 171 insertions(+), 72 deletions(-) diff --git a/drivers/mtd/spi-nor/phytium-quadspi.c b/drivers/mtd/spi-nor/phytium-quadspi.c index 6a415c2ed1..815c51f8b5 100644 --- a/drivers/mtd/spi-nor/phytium-quadspi.c +++ b/drivers/mtd/spi-nor/phytium-quadspi.c @@ -25,15 +25,15 @@ #define QSPI_FLASH_CAP_REG 0x000 #define QSPI_RD_CFG_REG 0x004 #define QSPI_WR_CFG_REG 0x008 -#define QSPI_FLUSH_REG 0x00C +#define QSPI_FLUSH_REG 0x00C #define QSPI_CMD_PORT_REG 0x010 #define QSPI_ADDR_PORT_REG 0x014 #define QSPI_HD_PORT_REG 0x018 #define QSPI_LD_PORT_REG 0x01C #define QSPI_FUN_SET_REG 0x020 -#define QSPI_WIP_REG 0x024 -#define QSPI_WP_REG 0x028 -#define QSPI_MODE_REG 0x02C +#define QSPI_WIP_REG 0x024 +#define QSPI_WP_REG 0x028 +#define QSPI_MODE_REG 0x02C #define QSPI_FLASH_CAP_NUM_SHIFT 3 #define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT) @@ -142,7 +142,8 @@ #define PHYTIUM_QSPI_FIFO_TIMEOUT_US 50000 #define PHYTIUM_QSPI_BUSY_TIMEOUT_US 100000 -#define PHYTIUM_SCK_SEL 0x05 +#define PHYTIUM_SCK_SEL_MIN 0x03 /*Max frequency 37.5MHZ i.e. 8 div*/ +#define PHYTIUM_SCK_SEL 0x05 /*Default frequency 9.385MHZ i.e. 32 div*/ #define PHYTIUM_CMD_SCK_SEL 0x07 #define PHYTIUM_FMODE_MM 0x01 @@ -156,28 +157,28 @@ #define CMD_RDSR 0x05 #define CMD_WREN 0x06 #define CMD_RDAR 0x65 -#define CMD_P4E 0x20 -#define CMD_4P4E 0x21 +#define CMD_P4E 0x20 +#define CMD_4P4E 0x21 #define CMD_BE 0x60 -#define CMD_4BE 0xC7 -#define CMD_READ 0x03 -#define CMD_FAST_READ 0x0B -#define CMD_QOR 0x6B -#define CMD_QIOR 0xEB -#define CMD_DDRFR 0x0D -#define CMD_DDRQIOQ 0xED -#define CMD_PP 0x02 -#define CMD_QPP 0x32 -#define CMD_SE 0xD8 -#define CMD_4FAST_READ 0x0C -#define CMD_4READ 0x13 -#define CMD_4QOR 0x6C -#define CMD_4QIOR 0xEC -#define CMD_4DDRFR 0x0E -#define CMD_4DDRQIOR 0xEE -#define CMD_4PP 0x12 -#define CMD_4QPP 0x34 -#define CMD_4SE 0xDC +#define CMD_4BE 0xC7 +#define CMD_READ 0x03 +#define CMD_FAST_READ 0x0B +#define CMD_QOR 0x6B +#define CMD_QIOR 0xEB +#define CMD_DDRFR 0x0D +#define CMD_DDRQIOQ 0xED +#define CMD_PP 0x02 +#define CMD_QPP 0x32 +#define CMD_SE 0xD8 +#define CMD_4FAST_READ 0x0C +#define CMD_4READ 0x13 +#define CMD_4QOR 0x6C +#define CMD_4QIOR 0xEC +#define CMD_4DDRFR 0x0E +#define CMD_4DDRQIOR 0xEE +#define CMD_4PP 0x12 +#define CMD_4QPP 0x34 +#define CMD_4SE 0xDC #define PHYTIUM_QSPI_1_1_1 0 #define PHYTIUM_QSPI_1_1_2 1 @@ -308,7 +309,7 @@ static int phytium_qspi_write_enable(struct phytium_qspi *qspi, u32 cmd = 0; cmd = CMD_WREN << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); @@ -323,7 +324,7 @@ static int phytium_qspi_write_disable(struct phytium_qspi *qspi, u32 cmd = 0; cmd = CMD_WRDI << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); @@ -365,7 +366,7 @@ static int phytium_qspi_read_flash_sfdp(struct phytium_qspi *qspi, cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); cmd |= BIT(QSPI_CMD_PORT_P_BUFFER_SHIFT); cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); @@ -480,6 +481,46 @@ static int phytium_qspi_write_reg(struct spi_nor *nor, u8 opcode, return 0; } +static int phytium_qspi_get_transfer(enum spi_nor_protocol proto) +{ + int transfer = 0; + /* QSPI protocol */ + switch (proto) { + case SNOR_PROTO_1_1_1: + transfer = PHYTIUM_QSPI_1_1_1; + break; + + case SNOR_PROTO_1_1_2: + transfer = PHYTIUM_QSPI_1_1_2; + break; + + case SNOR_PROTO_1_1_4: + transfer = PHYTIUM_QSPI_1_1_4; + break; + + case SNOR_PROTO_1_2_2: + transfer = PHYTIUM_QSPI_1_2_2; + break; + + case SNOR_PROTO_1_4_4: + transfer = PHYTIUM_QSPI_1_4_4; + break; + + case SNOR_PROTO_2_2_2: + transfer = PHYTIUM_QSPI_2_2_2; + break; + + case SNOR_PROTO_4_4_4: + transfer = PHYTIUM_QSPI_4_4_4; + break; + + default: + break; + } + + return transfer; +} + static ssize_t phytium_qspi_read_tmp(struct phytium_qspi *qspi, u32 read_cmd, loff_t from, size_t len, u_char *buf) { @@ -507,6 +548,7 @@ static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, struct phytium_qspi *qspi = flash->qspi; u32 cmd = nor->read_opcode; u32 addr = (u32)from; + u32 transfer = PHYTIUM_QSPI_1_1_1; addr = addr + flash->cs * flash->fsize; dev_dbg(qspi->dev, "read(%#.2x): buf:%pK from:%#.8x len:%#zx\n", @@ -517,7 +559,8 @@ static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd &= ~QSPI_RD_CFG_RD_TRANSFER_MASK; - cmd |= (flash->addr_width << QSPI_RD_CFG_RD_TRANSFER_SHIFT); + transfer = phytium_qspi_get_transfer(nor->read_proto); + cmd |= transfer << QSPI_RD_CFG_RD_TRANSFER_SHIFT; switch (nor->read_opcode) { case CMD_READ: @@ -567,55 +610,82 @@ static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, struct phytium_qspi *qspi = flash->qspi; u32 cmd = nor->program_opcode; u32 addr = (u32)to; + u32 transfer = PHYTIUM_QSPI_1_1_1; int i; u_char tmp[8] = {0}; size_t mask = 0x03; - - addr = addr + flash->cs * flash->fsize; - dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#zx\n", - nor->program_opcode, buf, addr, len); + size_t mask_p = 0x07; + u8 len_p; + u32 cnt = 0; + u32 val = 0; if (addr & 0x03) { dev_err(dev, "Addr not four-byte aligned!\n"); return -EINVAL; } - cmd = cmd << QSPI_WR_CFG_WR_CMD_SHIFT; - cmd |= BIT(QSPI_WR_CFG_WR_MODE_SHIFT); - cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd = 0; + cmd |= nor->program_opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); - switch (nor->program_opcode) { - case CMD_PP: - case CMD_QPP: - cmd &= ~QSPI_WR_CFG_WR_ADDR_SEL_MASK; - break; - case CMD_4PP: - case CMD_4QPP: - cmd |= BIT(QSPI_WR_CFG_WR_ADDR_SEL_SHIFT); - break; - default: - dev_err(qspi->dev, "Not support program command:%#x\n", - nor->erase_opcode); - return -EINVAL; - } + cmd &= ~QSPI_CMD_PORT_TRANSFER_MASK; + transfer = phytium_qspi_get_transfer(nor->write_proto); + cmd |= transfer << QSPI_CMD_PORT_TRANSFER_SHIFT; - dev_dbg(qspi->dev, "write cmd:%x\n", cmd); - writel_relaxed(cmd, qspi->io_base + QSPI_WR_CFG_REG); + switch (nor->addr_width) { + case 3: + cmd &= ~QSPI_CMD_PORT_SEL_MASK; + break; + case 4: + cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); + break; + default: + dev_err(qspi->dev, "Not support addr_width:%#x\n", + nor->addr_width); + return -EINVAL; + } - for (i = 0; i < len/4; i++) - writel_relaxed(*(u32 *)(buf + 4*i), qspi->mm_base + addr + 4*i); + cmd |= flash->clk_div & QSPI_CMD_PORT_SCK_SEL_MASK; + cmd |= 0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT; - if (len & mask) { - addr = addr + (len & ~mask); - phytium_qspi_read_tmp(qspi, flash->read_cmd, addr, 4, &tmp[0]); - memcpy(tmp, buf + (len & ~mask), len & mask); - writel_relaxed(*(u32 *)(tmp), qspi->mm_base + addr); + for (i = 0; i < len / 8; i++) { + val = readl_relaxed(qspi->io_base + QSPI_WR_CFG_REG); + phytium_qspi_write_enable(qspi,flash); + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + writel_relaxed(*(u32 *)(buf + 4 + 8 * i),qspi->io_base + QSPI_HD_PORT_REG); + writel_relaxed(*(u32 *)(buf + 8 * i),qspi->io_base + QSPI_LD_PORT_REG); + phytium_qspi_wait_cmd(qspi, flash); + phytium_qspi_write_disable(qspi,flash); + addr += 8; } - writel_relaxed(QSPI_FLUSH_EN, qspi->io_base + QSPI_FLUSH_REG); + len_p = (u8)(len & mask_p); + if (len_p) { + val = readl_relaxed(qspi->io_base + QSPI_WR_CFG_REG); + phytium_qspi_write_enable(qspi,flash); + cmd &= ~(0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT); + cmd |= (len_p - 1) << QSPI_CMD_PORT_RW_NUM_SHIFT; + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + if ((len_p - 4) > 0) { + memcpy(tmp, buf + 4 + (len / 8) * 8, len_p - 4); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_HD_PORT_REG); + memcpy(tmp, buf + (len / 8) * 8, 4); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_LD_PORT_REG); + } else if (len_p == 4) { + memcpy(tmp, buf + (len / 8) * 8, 4); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_LD_PORT_REG); + } else { + memcpy(tmp, buf + (len / 8) * 8, len & mask); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_LD_PORT_REG); + } + } + writel_relaxed(0x05000000, qspi->io_base + QSPI_WR_CFG_REG); phytium_qspi_wait_cmd(qspi, flash); - return len; } @@ -631,7 +701,7 @@ static int phytium_qspi_erase(struct spi_nor *nor, loff_t offs) phytium_qspi_write_enable(qspi, flash); cmd = cmd << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; /* s25fl256s1 not supoort D8, DC, 20, 21 */ @@ -761,8 +831,12 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, if (!clk_div) clk_div = PHYTIUM_SCK_SEL; - if (clk_div < 4) - return -EINVAL; + if (clk_div < PHYTIUM_SCK_SEL_MIN) { + clk_div = PHYTIUM_SCK_SEL_MIN; + dev_warn(qspi->dev, + "spi-clk-div too small, use default value: %d .\n", + clk_div); + } presc = DIV_ROUND_UP(qspi->clk_rate, max_rate) - 1; @@ -779,6 +853,16 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, } else if (width != 1) return -EINVAL; + // add tx hwcaps + of_property_read_u32(np, "spi-tx-bus-width", &width); + if (!width) + width = 1; + + if (width == 4) { + hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4 | SNOR_HWCAPS_PP_1_4_4 | + SNOR_HWCAPS_PP_4_4_4; + } + flash = &qspi->flash[cs_num]; flash->qspi = qspi; flash->cs = cs_num; @@ -817,6 +901,7 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, flash_cap = cs_num << QSPI_FLASH_CAP_NUM_SHIFT; flash_cap |= ret; writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + writel_relaxed(0x05000000, qspi->io_base + QSPI_WR_CFG_REG); flash->read_mode = PHYTIUM_FMODE_MM; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 590d5212cc..2f70f3a96d 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -89,6 +89,7 @@ struct flash_info { #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define USE_CLSR BIT(14) /* use CLSR command */ +#define SPI_NOR_QUAD_WRITE BIT(15) /* Flash supports Quad Write */ int (*quad_enable)(struct spi_nor *nor); }; @@ -1063,25 +1064,29 @@ static const struct flash_info spi_nor_ids[] = { { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { "gd25b512me", INFO(0xc8471a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { "gd55b01ge", INFO(0xc8471b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { "gd25lb512me", INFO(0xc8671a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { @@ -1283,13 +1288,16 @@ static const struct flash_info spi_nor_ids[] = { { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25m512jvq", INFO(0xef4020, 0, 64 * 1024, 1024, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES) + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES | + SPI_NOR_QUAD_WRITE) }, { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES) + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES | + SPI_NOR_QUAD_WRITE) }, { "w25m512jvsfiq", INFO(0xef4021, 0, 64 * 1024, 2048, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES) + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES | + SPI_NOR_QUAD_WRITE) }, /* Catalyst / On Semiconductor -- non-JEDEC */ @@ -2542,6 +2550,12 @@ static int spi_nor_init_params(struct spi_nor *nor, spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP, SNOR_PROTO_1_1_1); + if (info->flags & SPI_NOR_QUAD_WRITE) { + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4); + } + /* Select the procedure to set the Quad Enable bit. */ if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD | SNOR_HWCAPS_PP_QUAD)) { -- Gitee From 2028aa9b93035715e1973e04b9792d9f19c9ae93 Mon Sep 17 00:00:00 2001 From: hujun Date: Tue, 17 Oct 2023 11:36:44 +0800 Subject: [PATCH 07/41] tacho-phytium:add edges per revolution --- drivers/hwmon/tacho-phytium.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/tacho-phytium.c b/drivers/hwmon/tacho-phytium.c index 4f5d671a29..a2863f6e81 100644 --- a/drivers/hwmon/tacho-phytium.c +++ b/drivers/hwmon/tacho-phytium.c @@ -76,6 +76,7 @@ struct phytium_tacho { u8 work_mode; u8 edge_mode; u32 debounce; + u32 per_edges; }; static u16 capture_count; @@ -140,7 +141,7 @@ static int phytium_get_fan_tach_rpm(struct phytium_tacho *priv) if (raw_data == 0) return 0; - return (clk_source * 60 * raw_data) / 0x2faf080 / tach_div; + return (clk_source * 60 * raw_data) / 0x2faf080 / tach_div / priv->per_edges; } static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, @@ -267,11 +268,23 @@ static int phytium_tacho_get_debounce(struct phytium_tacho *tacho) return 0; } +static int phytium_tacho_get_edges_per_revolution(struct phytium_tacho *tacho) +{ + u32 value; + struct device_node *nc = tacho->dev->of_node; + + if (!of_property_read_u32(nc, "edges-per-revolution", &value)) + return value; + else + return 1; +} + static void phytium_tacho_get_of_data(struct phytium_tacho *tacho) { tacho->work_mode = phytium_tacho_get_work_mode(tacho); tacho->edge_mode = phytium_tacho_get_edge_mode(tacho); tacho->debounce = phytium_tacho_get_debounce(tacho); + tacho->per_edges = phytium_tacho_get_edges_per_revolution(tacho); } static int phytium_tacho_probe(struct platform_device *pdev) -- Gitee From bc50aeda028afb4678c00ca1863653d14363bfc7 Mon Sep 17 00:00:00 2001 From: hujun Date: Wed, 20 Dec 2023 15:18:44 +0800 Subject: [PATCH 08/41] pe220x-dtsi:add mio mac1 and remove adc1 Signed-off-by: hujun --- arch/arm64/boot/dts/phytium/pe2201.dtsi | 214 +++++++++++++++++++----- 1 file changed, 176 insertions(+), 38 deletions(-) diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index 49721462ea..5cedfa7c8c 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -13,6 +13,7 @@ / { aliases { ethernet0 = &macb0; ethernet1 = &macb1; + ethernet2 = &macb2; }; }; @@ -53,6 +54,166 @@ i2c2: i2c@28013000 { status = "disabled"; }; + mio0: i2c@28014000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28014000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio1: i2c@28016000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28016000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio2: i2c@28018000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28018000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio3: i2c@2801a000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2801a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio4: i2c@2801c000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2801c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio5: i2c@2801e000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2801e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio6: i2c@28020000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28020000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio7: i2c@28022000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28022000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio8: i2c@28024000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio10: i2c@28028000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28028000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio11: i2c@2802a000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2802a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio12: i2c@2802c000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2802c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio13: i2c@2802e000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2802e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mio15: i2c@28032000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + onewire0: onewire@2803f000 { compatible = "phytium,w1"; reg = <0x0 0x2803f000 0x0 0x1000>; @@ -180,42 +341,6 @@ channel@7 { }; }; - adc1: adc@2807c000 { - compatible = "phytium,adc"; - reg = <0x0 0x2807c000 0x0 0x1000>; - interrupts = ; - clocks = <&sysclk_50mhz>; - status = "disabled"; - - #address-cells = <1>; - #size-cells = <0>; - - channel@0 { - reg = <0>; - }; - channel@1 { - reg = <1>; - }; - channel@2 { - reg = <2>; - }; - channel@3 { - reg = <3>; - }; - channel@4 { - reg = <4>; - }; - channel@5 { - reg = <5>; - }; - channel@6 { - reg = <6>; - }; - channel@7 { - reg = <7>; - }; - }; - sgpio: sgpio@2807d000 { compatible = "phytium,sgpio"; reg = <0x0 0x2807d000 0x0 0x1000>; @@ -228,7 +353,20 @@ sgpio: sgpio@2807d000 { status = "disabled"; }; - macb0: ethernet@32010000 { + macb0: ethernet@3200e000 { + compatible = "cdns,phytium-gem"; + reg = <0x0 0x3200e000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb1: ethernet@32010000 { compatible = "cdns,phytium-gem"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , @@ -241,7 +379,7 @@ macb0: ethernet@32010000 { status = "disabled"; }; - macb1: ethernet@32012000 { + macb2: ethernet@32012000 { compatible = "cdns,phytium-gem"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , -- Gitee From 9d7c5f3fc298e494d71ee4aac2ed237744c82781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=A1=90=E5=87=A4?= Date: Thu, 31 Aug 2023 03:51:20 +0000 Subject: [PATCH 09/41] net macb: add phy reset function --- drivers/net/ethernet/cadence/macb.h | 8 + drivers/net/ethernet/cadence/macb_main.c | 295 +++++++++++++++++++++++ 2 files changed, 303 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 570d2ddf47..bfdc374d1e 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -14,6 +14,7 @@ #include #include #include +#include #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) #define MACB_EXT_DESC @@ -1282,6 +1283,13 @@ struct macb { int tx_bd_rd_prefetch; u32 rx_intr_mask; + + void *phy_vbase_addr; + unsigned long crc_detect_period; /* default period: 0 ms; disable crc detect function */ + unsigned long nb_rx; /* CRC detection starts when more than nb_rx packets are received */ + unsigned long crc_err_percent; /* PHY RESET when crc error more than crc_err_percent */ + unsigned long phy_reset_times; + struct task_struct *poll_task; }; #ifdef CONFIG_MACB_USE_HWSTAMP diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 0f18b5771c..558c12efe0 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include "macb.h" #define MACB_RX_BUFFER_SIZE 128 @@ -93,6 +96,193 @@ */ #define MACB_HALT_TIMEOUT 1230 +#define false 0 +#define true 1 +#define DEFAULT_SLEEP_S 1 +#define DEFAULT_CHECK_FCS_PERIOD_MS 2000 +#define DEFAULT_CHECK_FCS_RX_THRESHOLD 2 +#define DEFAULT_CHECK_FCS_PERCENT_THRESHOLD 1 + +#define PHY0_ADDR_BASE 0x32140000 +#define PHY0_ADDR_OFFSET 0x254 +#define PHY1_ADDR_BASE 0x32240000 +#define PHY1_ADDR_OFFSET 0x08c +#define PHY2_ADDR_BASE 0x32340000 +#define PHY2_ADDR_OFFSET 0x254 +#define PHY3_ADDR_BASE 0x32440000 +#define PHY3_ADDR_OFFSET 0x254 + +#define MACB0_ADDR_BASE 0x3200c000 +#define MACB1_ADDR_BASE 0x3200e000 +#define MACB2_ADDR_BASE 0x32010000 +#define MACB3_ADDR_BASE 0x32012000 + +#define MAP_SIZE 4096 + +static DEFINE_MUTEX(stats_mutex); +static int crc_detect_poll(void *args); + +static ssize_t +show_check_fcs_period(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->crc_detect_period); +} + +static ssize_t +store_check_fcs_period(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long crc_detect_period; + + if (0 != kstrtoul(buf, 0, &crc_detect_period)) + return -EINVAL; + + if (crc_detect_period < 0) + return -EINVAL; + bp->crc_detect_period = crc_detect_period; + + return err ? err : count; +} +static DEVICE_ATTR(check_fcs_period, S_IRUGO | S_IWUSR, show_check_fcs_period, store_check_fcs_period); + +static ssize_t +show_check_fcs_rx_threshold(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->nb_rx); +} + +static ssize_t +store_check_fcs_rx_threshold(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long nb_rx; + + if (0 != kstrtoul(buf, 0, &nb_rx)) + return -EINVAL; + + if (nb_rx < 0) + return -EINVAL; + bp->nb_rx = nb_rx; + + return err ? err : count; +} +static DEVICE_ATTR(check_fcs_rx_threshold, S_IRUGO | S_IWUSR, show_check_fcs_rx_threshold, store_check_fcs_rx_threshold); + +static ssize_t +show_check_fcs_percent_threshold(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->crc_err_percent); +} + +static ssize_t +store_check_fcs_percent_threshold(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long crc_err_percent; + + if (0 != kstrtoul(buf, 0, &crc_err_percent)) + return -EINVAL; + + if (crc_err_percent > 100 || crc_err_percent < 0) + return -EINVAL; + bp->crc_err_percent = crc_err_percent; + + return err ? err : count; +} +static DEVICE_ATTR(check_fcs_percent_threshold, S_IRUGO | S_IWUSR, show_check_fcs_percent_threshold, store_check_fcs_percent_threshold); + +static ssize_t +show_phy_reset_times(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->phy_reset_times); +} +static DEVICE_ATTR(phy_reset_times, S_IRUGO, show_phy_reset_times, NULL); + +static struct attribute *dev_attrs[] = { + &dev_attr_check_fcs_period.attr, + &dev_attr_check_fcs_rx_threshold.attr, + &dev_attr_check_fcs_percent_threshold.attr, + &dev_attr_phy_reset_times.attr, + NULL, +}; + +static const struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +static void +*get_mmap_addr(unsigned long phy_addr) +{ + void *virt_addr; + + virt_addr = ioremap(phy_addr, MAP_SIZE); + + if (!virt_addr) { + pr_err("Failed to map physical address to virtual address\n"); + return NULL; + } + + return virt_addr; +} + +static void +phy_base_mmap(struct net_device *dev) { + struct macb *bp = netdev_priv(dev); + void *base; + + switch (dev->base_addr) { + case MACB0_ADDR_BASE: + base = get_mmap_addr(PHY0_ADDR_BASE); + break; + case MACB1_ADDR_BASE: + base = get_mmap_addr(PHY1_ADDR_BASE); + break; + case MACB2_ADDR_BASE: + base = get_mmap_addr(PHY2_ADDR_BASE); + break; + case MACB3_ADDR_BASE: + base = get_mmap_addr(PHY3_ADDR_BASE); + break; + default: + break; + } + + bp->phy_vbase_addr = base; +} + +static void +phy_base_unmap(struct net_device *dev) { + struct macb *bp = netdev_priv(dev); + + if (bp->phy_vbase_addr) + iounmap(bp->phy_vbase_addr); +} + /* DMA buffer descriptor might be different size * depends on hardware configuration: * @@ -2673,6 +2863,13 @@ static int macb_open(struct net_device *dev) if (bp->ptp_info) bp->ptp_info->ptp_init(dev); + bp->phy_reset_times = 0; + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + bp->poll_task = kthread_create(crc_detect_poll, dev, "crc_poll_%lx", dev->base_addr); + kthread_bind(bp->poll_task, 0); + wake_up_process(bp->poll_task); + } + return 0; } @@ -2683,6 +2880,9 @@ static int macb_close(struct net_device *dev) unsigned long flags; unsigned int q; + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) + kthread_stop(bp->poll_task); + netif_tx_stop_all_queues(dev); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) @@ -2748,6 +2948,7 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) struct gem_stats *hwstat = &bp->hw_stats.gem; struct net_device_stats *nstat = &bp->dev->stats; + mutex_lock(&stats_mutex); gem_update_stats(bp); nstat->rx_errors = (hwstat->rx_frame_check_sequence_errors + @@ -2777,6 +2978,7 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) nstat->tx_aborted_errors = hwstat->tx_excessive_collisions; nstat->tx_carrier_errors = hwstat->tx_carrier_sense_errors; nstat->tx_fifo_errors = hwstat->tx_underrun; + mutex_unlock(&stats_mutex); return nstat; } @@ -2787,9 +2989,11 @@ static void gem_get_ethtool_stats(struct net_device *dev, struct macb *bp; bp = netdev_priv(dev); + mutex_lock(&stats_mutex); gem_update_stats(bp); memcpy(data, &bp->ethtool_stats, sizeof(u64) * (GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES)); + mutex_unlock(&stats_mutex); } static int gem_get_sset_count(struct net_device *dev, int sset) @@ -3523,6 +3727,88 @@ static void macb_configure_caps(struct macb *bp, dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); } +/* only reset phy whose mode is sgmii */ +static void +phy_reset(struct net_device *dev) { + struct macb *bp = netdev_priv(dev); + + if (bp->phy_vbase_addr) { + switch (dev->base_addr) { + case MACB0_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); + break; + case MACB1_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); + break; + case MACB2_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); + break; + case MACB3_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); + break; + default: + break; + } + } +} + +static int crc_detect_poll(void *args) +{ + struct net_device *dev = (struct net_device *)args; + struct macb *bp = netdev_priv(dev); + struct net_device_stats *macb_stats; + unsigned long rx_pkts; + unsigned long rx_pkts_prev; + unsigned long num_crc_err; + unsigned long num_crc_err_prev; + unsigned long percent; + unsigned long rx_pkts_total; + int flag = false; + + phy_base_mmap(dev); + + while (!kthread_should_stop()) { + if (!(bp->link) || (bp->crc_detect_period == 0)) { + ssleep(DEFAULT_SLEEP_S); + continue; + } + + /* 1. detect num of detected rx_pkts + * 2. detect percent of crc error + * 3. phy reset + */ + macb_stats = macb_get_stats(dev); + if (!flag) { + rx_pkts_prev = macb_stats->rx_packets; + num_crc_err_prev = macb_stats->rx_crc_errors; + flag = true; + } + + rx_pkts = macb_stats->rx_packets - rx_pkts_prev; + num_crc_err = macb_stats->rx_crc_errors - num_crc_err_prev; + rx_pkts_total = rx_pkts + num_crc_err; + if ((rx_pkts_total >= bp->nb_rx) && (rx_pkts_total != 0)) { + percent = (num_crc_err * 100) / rx_pkts_total; + if (percent >= bp->crc_err_percent) { + phy_reset(dev); + bp->phy_reset_times++; + } + } + + rx_pkts_prev += rx_pkts; + num_crc_err_prev += num_crc_err; + + msleep(bp->crc_detect_period); + } + + phy_base_unmap(dev); + return 0; +} + static void macb_probe_queues(void __iomem *mem, bool native_io, unsigned int *queue_mask, @@ -4348,6 +4634,9 @@ static int macb_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); + bp->crc_detect_period = DEFAULT_CHECK_FCS_PERIOD_MS; + bp->nb_rx = DEFAULT_CHECK_FCS_RX_THRESHOLD; + bp->crc_err_percent = DEFAULT_CHECK_FCS_PERCENT_THRESHOLD; bp->pdev = pdev; bp->dev = dev; bp->regs = mem; @@ -4376,6 +4665,10 @@ static int macb_probe(struct platform_device *pdev) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); + err = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp); + if (err) + return err; + spin_lock_init(&bp->lock); /* setup capabilities */ @@ -4528,6 +4821,8 @@ static int macb_remove(struct platform_device *pdev) struct macb *bp; struct device_node *np = pdev->dev.of_node; + sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp); + dev = platform_get_drvdata(pdev); if (dev) { -- Gitee From b6a49243429407a483d4780ea3533eb2f485db47 Mon Sep 17 00:00:00 2001 From: wangzc Date: Tue, 6 Feb 2024 18:12:47 +0800 Subject: [PATCH 10/41] jpeg: Fix resolution switching bug After receiving the timer30 interrupt, add a flag to prevent the CPU core from operating the jpeg controller Signed-off-by: wangmin --- drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c index dde1929ba6..b6937fa4c9 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c @@ -900,7 +900,6 @@ static void phytium_jpeg_irq_res_change(struct phytium_jpeg_dev *jpeg_dev, ulong delay) { dev_info(jpeg_dev->dev, "Source resolution is changed, resetting\n"); - set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); phytium_jpeg_off(jpeg_dev); @@ -1117,6 +1116,7 @@ static irqreturn_t phytium_jpeg_timer30_irq(int irq, void *arg) /* call SE to poweroff JPEG Engine */ arm_smccc_smc(0xc300fff4, 0x9, 0x2, 0x80000020, 0, 0, 0, 0, &res); + set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); /* set JPEG Engine's status is poweroff */ set_bit(VIDEO_POWEROFF, &jpeg_dev->status); dev_info(jpeg_dev->dev, "timer30 set jpeg status 0x%lx\n", jpeg_dev->status); -- Gitee From ad7cd9741cfdcf8a62c3970240eb319aed53f237 Mon Sep 17 00:00:00 2001 From: wangzc Date: Tue, 16 Apr 2024 09:55:02 +0800 Subject: [PATCH 11/41] watchdog:sbsa_gwdt:Add reset-type attribute. There are three reset methods: "wdt reset","core reset" and "none". Default to "core reset". Signed-off-by: wangzc --- arch/arm64/boot/dts/phytium/pe220x.dtsi | 6 ++++-- drivers/watchdog/sbsa_gwdt.c | 28 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index dd104afeb2..49f251b745 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -516,7 +516,8 @@ spi3: spi@2803d000 { watchdog0: watchdog@28040000 { compatible = "arm,sbsa-gwdt"; reg = <0x0 0x28041000 0x0 0x1000>, - <0x0 0x28040000 0x0 0x1000>; + <0x0 0x28040000 0x0 0x1000>, + <0x0 0x32a11810 0x0 0x0002>; interrupts = ; timeout-sec = <30>; status = "disabled"; @@ -525,7 +526,8 @@ watchdog0: watchdog@28040000 { watchdog1: watchdog@28042000 { compatible = "arm,sbsa-gwdt"; reg = <0x0 0x28043000 0x0 0x1000>, - <0x0 0x28042000 0x0 0x1000>; + <0x0 0x28042000 0x0 0x1000>, + <0x0 0x32a11812 0x0 0x0002>; interrupts = ; timeout-sec = <30>; status = "disabled"; diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index e8bd9887c5..4c3b991f28 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -82,6 +82,11 @@ #define SBSA_GWDT_WCS_WS0 BIT(1) #define SBSA_GWDT_WCS_WS1 BIT(2) +/* Watchdog Reset type */ +#define SBSA_GWDT_RESET 0x0 +#define SBSA_GWDT_CORE_RESET 0x1 +#define SBSA_GWDT_NONE 0x2 + /** * struct sbsa_gwdt - Internal representation of the SBSA GWDT * @wdd: kernel watchdog_device structure @@ -234,6 +239,9 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) struct resource *res; int ret, irq; u32 status; + struct device_node *np; + const char *reset_type; + void __iomem *shared_base; gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); if (!gwdt) @@ -313,6 +321,26 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) */ sbsa_gwdt_set_timeout(wdd, wdd->timeout); + + np = pdev->dev.of_node; + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + + shared_base = devm_ioremap_resource(dev, res); + if (IS_ERR(shared_base)) + return PTR_ERR(shared_base); + + ret = of_property_read_string(np, "reset-type", &reset_type); + if (ret) + writew(SBSA_GWDT_CORE_RESET, shared_base); + else { + if (!strcmp(reset_type, "wdt reset")) + writew(SBSA_GWDT_RESET, shared_base); + else if (!strcmp(reset_type, "core reset")) + writew(SBSA_GWDT_CORE_RESET, shared_base); + else if (!strcmp(reset_type, "none")) + writew(SBSA_GWDT_NONE, shared_base); + } + ret = watchdog_register_device(wdd); if (ret) return ret; -- Gitee From 8eeb917040f74b5a9c60a00602c0df775ca6d12d Mon Sep 17 00:00:00 2001 From: Chen Zhenhua Date: Wed, 10 Jan 2024 10:27:31 +0800 Subject: [PATCH 12/41] usb: phytium: Add endpoints management mechanism Because the IP only implements 6 endpoints, some peripheral endpoint values are greater than 6, and it is necessary to add endpoint management mechanisms to manage endpoints, otherwise some devices may not work properly. Signed-off-by: Chen Zhenhua Change-Id: I2b08a956e36db5ec2f4a8f4ff085df2c58007d29 --- drivers/usb/phytium/host.c | 172 +++++++++++++++++++++++++-------- drivers/usb/phytium/host_api.h | 3 + 2 files changed, 135 insertions(+), 40 deletions(-) diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index d1b82adc91..8868e15cfc 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -23,6 +23,71 @@ #define HOST_EP_NUM 16 +static int get_epnum_from_pool(struct HOST_CTRL *priv, int real_epNum, bool dirIn) +{ + int index, dir = 0; + int ret = 0; + + if (!priv) + return 0; + + if (!dirIn) + dir = 1; + + if (real_epNum <= MAX_INSTANCE_EP_NUM) { + if (!priv->ep_remap_pool[dir][real_epNum]) { + priv->ep_remap_pool[dir][real_epNum] = real_epNum; + ret = real_epNum; + goto out; + } + + if (priv->ep_remap_pool[dir][real_epNum] == real_epNum) { + ret = real_epNum; + goto out; + } + } else { + for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) { + if (priv->ep_remap_pool[dir][index] == real_epNum) { + ret = index; + goto out; + } + } + + for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) { + if (!priv->ep_remap_pool[dir][index]) { + priv->ep_remap_pool[dir][index] = real_epNum; + ret = index; + goto out; + } + } + } + +out: + return ret; +} + +static int release_epnum_from_pool(struct HOST_CTRL *priv, int real_epNum, bool dirIn) +{ + int index = 0; + int dir = 0; + + if (!priv) + return 0; + + if (!dirIn) + dir = 1; + + for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) { + if (priv->ep_remap_pool[dir][index] == real_epNum) { + priv->ep_remap_pool[dir][index] = 0; + + return 0; + } + } + + return 0; +} + static inline struct HOST_REQ *getUsbRequestEntry(struct list_head *list) { return (struct HOST_REQ *)((uintptr_t)list - (uintptr_t)&(((struct HOST_REQ *)0)->list)); @@ -85,6 +150,7 @@ static inline void disconnectHostDetect(struct HOST_CTRL *priv) if (!priv) return; + memset(priv->ep_remap_pool, 0, sizeof(priv->ep_remap_pool)); otgctrl = phytium_read8(&priv->regs->otgctrl); if ((otgctrl & OTGCTRL_ASETBHNPEN) && priv->otgState == HOST_OTG_STATE_A_SUSPEND) pr_info("Device no Response\n"); @@ -571,7 +637,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, regCon); if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { retval = priv->hostCallbacks.getEpToggle(priv, - usbReq->usbDev, usbEpPriv->epNum, 0); + usbReq->usbDev, usbHEp->device_epNum, 0); if (retval) { phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | ENDPRST_IO_TX); @@ -592,7 +658,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, usbEpPriv->maxPacketSize); phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].txctrl, - usbEpPriv->epNum); + usbHEp->device_epNum); phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); @@ -629,7 +695,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { if (priv->hostCallbacks.getEpToggle) { retval = priv->hostCallbacks.getEpToggle(priv, - usbReq->usbDev, usbEpPriv->epNum, 1); + usbReq->usbDev, usbHEp->device_epNum, 1); if (retval) { phytium_write8(&priv->regs->endprst, hwEp->hwEpNum); phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | @@ -646,7 +712,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, usbEpPriv->maxPacketSize); phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].rxctrl, - usbEpPriv->epNum); + usbHEp->device_epNum); phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); @@ -778,6 +844,38 @@ static void hostStartReq(struct HOST_CTRL *priv, struct HOST_REQ *req) } } +static void abortTransfer(struct HOST_CTRL *priv, + struct HOST_REQ *usbReq, struct HostEp *hwEp) +{ + struct HOST_EP *usbEp; + struct HOST_EP_PRIV *usbHEpPriv; + uint32_t status; + + if (!priv || !usbReq || !hwEp || !hwEp->scheduledUsbHEp) + return; + + usbEp = hwEp->scheduledUsbHEp; + usbHEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv; + if (!usbHEpPriv) + return; + + status = (usbReq->status == EINPROGRESS) ? 0 : usbReq->status; + givebackRequest(priv, usbReq, status); + + if (list_empty(&usbEp->reqList)) { + usbHEpPriv->epIsReady = 0; + usbHEpPriv->currentHwEp = NULL; + hwEp->scheduledUsbHEp = NULL; + + if (hwEp->channel) { + priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel); + hwEp->channel = NULL; + } + + if (usb_endpoint_xfer_int(&usbEp->desc)) + list_del(&usbHEpPriv->node); + } +} static void scheduleNextTransfer(struct HOST_CTRL *priv, struct HOST_REQ *usbReq, struct HostEp *hwEp) @@ -805,7 +903,7 @@ static void scheduleNextTransfer(struct HOST_CTRL *priv, endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0; if (priv->hostCallbacks.setEpToggle) priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev, - usbHEpPriv->epNum, usbHEpPriv->isIn, endprst); + usbEp->device_epNum, usbHEpPriv->isIn, endprst); } else { if (waitForBusyBit(priv, hwEp) > 0) { usbReq->status = HOST_ESHUTDOWN; @@ -817,7 +915,7 @@ static void scheduleNextTransfer(struct HOST_CTRL *priv, endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0; if (priv->hostCallbacks.setEpToggle) priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev, - usbHEpPriv->epNum, usbHEpPriv->isIn, endprst); + usbEp->device_epNum, usbHEpPriv->isIn, endprst); } break; } @@ -1137,9 +1235,8 @@ static void host_endpoint_update(struct phytium_cusb *config, if (!config || !udev || !ep) return; - epnum = usb_endpoint_num(&ep->desc); - if (epnum > MAX_INSTANCE_EP_NUM) - epnum = MAX_INSTANCE_EP_NUM; + epnum = get_epnum_from_pool(config->host_priv, usb_endpoint_num(&ep->desc), + usb_endpoint_dir_in(&ep->desc)); if (usb_endpoint_dir_out(&ep->desc)) { if (udev->out_ep[epnum] == NULL) { @@ -1211,9 +1308,8 @@ static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) req->isoFramesDesc = NULL; req->isoFramesNumber = urb->number_of_packets; - req->epNum = usb_endpoint_num(host_ep_desc); - if (req->epNum > MAX_INSTANCE_EP_NUM) - req->epNum = MAX_INSTANCE_EP_NUM; + req->epNum = get_epnum_from_pool(config->host_priv, usb_endpoint_num(host_ep_desc), + usb_endpoint_dir_in(host_ep_desc)); if (usb_endpoint_dir_in(host_ep_desc)) { if (!usbDev->in_ep[req->epNum]) @@ -1248,6 +1344,7 @@ static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) req->status = EINPROGRESS; req->usbDev = &usbDev->udev; req->usbEp = req->epIsIn ? usbDev->in_ep[req->epNum] : usbDev->out_ep[req->epNum]; + req->usbEp->device_epNum = usb_endpoint_num(host_ep_desc); if (!req->epNum) usbDev->ep0_hep.desc.wMaxPacketSize = urb->dev->ep0.desc.wMaxPacketSize; @@ -1289,6 +1386,10 @@ static int hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto err_giveback; ret = config->host_obj->host_reqDequeue(priv, urb->hcpriv, status); + + release_epnum_from_pool(config->host_priv, usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe)); + kfree(urb->hcpriv); urb->hcpriv = NULL; done: @@ -1306,10 +1407,15 @@ static int hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) static void hc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ld_ep) { struct HOST_USB_DEVICE *usbDev; - int ep_num = usb_endpoint_num(&ld_ep->desc); + int ep_num; + struct phytium_cusb *config; - if (ep_num > MAX_INSTANCE_EP_NUM) - ep_num = MAX_INSTANCE_EP_NUM; + config = *(struct phytium_cusb **)hcd->hcd_priv; + if (!config) + return; + + ep_num = get_epnum_from_pool(config->host_priv, usb_endpoint_num(&ld_ep->desc), + usb_endpoint_dir_in(&ld_ep->desc)); usbDev = (struct HOST_USB_DEVICE *)ld_ep->hcpriv; if (!usbDev) @@ -1317,14 +1423,14 @@ static void hc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *l if (ld_ep->desc.bEndpointAddress) { if (usb_endpoint_dir_in(&ld_ep->desc)) { - if (!usbDev->in_ep[ep_num]) { + if (usbDev->in_ep[ep_num]) { usbDev->in_ep[ep_num]->userExt = NULL; INIT_LIST_HEAD(&usbDev->in_ep[ep_num]->reqList); kfree(usbDev->in_ep[ep_num]); usbDev->in_ep[ep_num] = NULL; } } else { - if (!usbDev->out_ep[ep_num]) { + if (usbDev->out_ep[ep_num]) { usbDev->out_ep[ep_num]->userExt = NULL; INIT_LIST_HEAD(&usbDev->out_ep[ep_num]->reqList); kfree(usbDev->out_ep[ep_num]); @@ -1667,6 +1773,7 @@ static uint32_t initEndpoints(struct HOST_CTRL *priv) priv->hwEpOutCount = 0; phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0); phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0); + memset(priv->ep_remap_pool, 0, sizeof(priv->ep_remap_pool)); for (epNum = 0; epNum < HOST_EP_NUM; epNum++) { priv->in[epNum].isInEp = 1; @@ -1917,8 +2024,7 @@ int32_t hostEpDisable(struct HOST_CTRL *priv, struct HOST_EP *ep) return 0; } -unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, - int speed) +unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, int speed) { unsigned int interval = 0; @@ -1932,7 +2038,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = 1 << interval; if ((1 << interval) != desc.bInterval) pr_info("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + 1 << interval, desc.bInterval); break; } @@ -1941,16 +2047,15 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = 1 << interval; if (interval != desc.bInterval - 1) pr_info("rounding to %d %sframes\n", 1 << interval, - speed == USB_SPEED_FULL ? "" : "micro"); + speed == USB_SPEED_FULL ? "" : "micro"); } break; case USB_SPEED_FULL: if (usb_endpoint_xfer_isoc(&desc)) { - interval = clamp_val(desc.bInterval, 1, 16) - 1; - if (interval != desc.bInterval - 1) + interval = clamp_val(desc.bInterval, 1, 16); + if (interval != desc.bInterval) pr_info("rounding to %d %sframes\n", 1 << interval, - speed == USB_SPEED_FULL ? "" : "micro"); - interval += 3; + speed == USB_SPEED_FULL ? "" : "micro"); break; } /* fall through */ @@ -1960,7 +2065,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = clamp_val(interval, 3, 10); if ((1 << interval) != desc.bInterval * 8) pr_info("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + 1 << interval, desc.bInterval); } } @@ -2053,8 +2158,6 @@ static int abortActuallyUsbRequest(struct HOST_CTRL *priv, { struct HOST_EP_PRIV *usbEpPriv; struct HostEp *hostEp; - uint16_t rxerrien = 0; - uint16_t txerrien = 0; uint8_t rxcon, txcon; if (!priv || !req || !usbEp) @@ -2064,31 +2167,20 @@ static int abortActuallyUsbRequest(struct HOST_CTRL *priv, hostEp = usbEpPriv->currentHwEp; usbEpPriv->transferFinished = 1; - if (hostEp->isInEp) { if (hostEp->hwEpNum) { rxcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon); rxcon = rxcon & (~BIT(7)); phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon, rxcon); } - rxerrien = phytium_read16(&priv->regs->rxerrien); - rxerrien &= ~(1 << hostEp->hwEpNum); - phytium_write16(&priv->regs->rxerrien, rxerrien); - phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | - ENDPRST_IO_TX | hostEp->hwEpNum); } else { if (hostEp->hwEpNum) { txcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon); txcon = txcon & (~BIT(7)); phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon, txcon); } - txerrien = phytium_read16(&priv->regs->txerrien); - txerrien &= ~(1 << hostEp->hwEpNum); - phytium_write16(&priv->regs->txerrien, txerrien); - phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | hostEp->hwEpNum); } - - scheduleNextTransfer(priv, req, hostEp); + abortTransfer(priv, req, hostEp); return 0; } diff --git a/drivers/usb/phytium/host_api.h b/drivers/usb/phytium/host_api.h index 3d45258278..b99d2b4980 100644 --- a/drivers/usb/phytium/host_api.h +++ b/drivers/usb/phytium/host_api.h @@ -10,6 +10,7 @@ #define MAX_SUPPORTED_DEVICES 16 #define USB_PORT_STAT_RESUME (1 << 31) #define MAX_INSTANCE_EP_NUM 6 +#define ENDPOINT_DIR 2 enum HOST_OtgState { HOST_OTG_STATE_A_IDLE, @@ -63,6 +64,7 @@ struct HOST_EP { struct list_head reqList; void *userExt; uint8_t *hcPriv; + uint8_t device_epNum; }; struct HOST_USB_DEVICE { @@ -241,6 +243,7 @@ struct HOST_CTRL { struct HOST_USB_DEVICE *host_devices_table[MAX_SUPPORTED_DEVICES]; struct CUSTOM_REGS *custom_regs; struct VHUB_REGS *vhub_regs; + int ep_remap_pool[ENDPOINT_DIR][MAX_INSTANCE_EP_NUM + 1]; }; struct HOST_OBJ *HOST_GetInstance(void); -- Gitee From 5fe71cb8383759a02439e9804b9ebf5cfc75fc62 Mon Sep 17 00:00:00 2001 From: hujun Date: Sun, 28 Apr 2024 14:03:17 +0800 Subject: [PATCH 13/41] phytium jpeg: Modified resolution update delay and irq status update modified resolution update to delay 200ms cleared all interrupts on jpeg poweroff and in timer30 irq. Support user selection mode, with optional modes including 'yuv444', or 'yuv422', or 'yuv420'. Signed-off-by: hujun Signed-off-by: wangzc --- .../platform/phytium-jpeg/phytium_jpeg_core.c | 129 +++++++++++++++--- .../platform/phytium-jpeg/phytium_jpeg_core.h | 3 +- 2 files changed, 112 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c index b6937fa4c9..1bda4a5d8f 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c @@ -2,7 +2,7 @@ /* * Driver for Phytium JPEG Encoder Engine * - * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + * Copyright (c) 2021, Phytium Technology Co., Ltd. */ #include "phytium_jpeg_reg.h" @@ -71,7 +71,13 @@ static u32 phytium_jpeg_header[PHYTIUM_JPEG_HEADER_SIZE] = { static char yuv_mode_str[YUV_MODE_STR_LEN] = { "yuv444" }; module_param_string(yuv_mode, yuv_mode_str, sizeof(yuv_mode_str), 0444); -MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as 'yuv444', or 'yuv422', or 'yuv420'. If no mode is set, the driver adapts defaults mode 'yuv444'."); +MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as" + " 'yuv444', or 'yuv422', or 'yuv420'. If no mode is set," + " the driver adapts defaults mode 'yuv444'."); + +/* The below global variables are used to fiter same log-print lines */ +static bool first_invalid = true; +static bool cur_non_zero = true; static u32 phytium_jpeg_read(struct phytium_jpeg_dev *jpeg_dev, u32 reg) { @@ -166,11 +172,6 @@ static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; - if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { - dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); - return; - } - /* disable all interrupt */ phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); /* clear all interrupt */ @@ -178,6 +179,11 @@ static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) /* disable JPEG engine */ phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { + dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); + return; + } + clear_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status); /* wait 50 ms */ mdelay(50); @@ -218,21 +224,28 @@ static void phytium_jpeg_get_resolution(struct phytium_jpeg_dev *jpeg_dev) if (width * height != 0) { detected_timings->width = width; detected_timings->height = height; + jpeg_dev->v4l2_input_status = 0; + cur_non_zero = true; + } else { + /* filter some repeated log-print lines */ + first_invalid = cur_non_zero; + cur_non_zero = false; } - jpeg_dev->v4l2_input_status = 0; - /* * Resolution is changed will trigger an interrupt, resolution detecting * also is disable during process interrupt. So re-enable. */ phytium_jpeg_enable_source_detecting(jpeg_dev); - dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); + + if (cur_non_zero == true || first_invalid == true) { + dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); + } } static void phytium_jpeg_set_resolution(struct phytium_jpeg_dev *jpeg_dev) { - struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; + struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; int i; int src_addrs[OCM_BUF_NUM]; /* @@ -479,6 +492,9 @@ static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { int ret; + u32 source_info; + u32 width; + u32 height; struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); /* @@ -500,6 +516,16 @@ static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, timings->type = V4L2_DV_BT_656_1120; timings->bt = jpeg_dev->detected_timings; + /* Get resolution from SRC_VGA_INFO_REG */ + source_info = phytium_jpeg_read(jpeg_dev, SRC_VGA_INFO_REG); + width = (source_info & SRC_HOR_PIXELS) >> SRC_WIDTH_SHIFT; + height = (source_info & SRC_VER_PIXELS) >> SRC_HEIGHT_SHIFT; + + /* Check if that the current resolution is zero. */ + if (width == 0 || height == 0) { + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + } + return jpeg_dev->v4l2_input_status ? -ENOLINK : 0; } @@ -738,8 +764,10 @@ static int phytium_jpeg_start_frame(struct phytium_jpeg_dev *jpeg_dev) unsigned long status; struct phytium_jpeg_buffer *jpeg_buf; + /* JPEG Engine shouldn't be enable to compress in the case no signal is input JPEG Engine. + * V4L2_IN_ST_NO_SIGNAL + */ if (jpeg_dev->v4l2_input_status) { - dev_err(jpeg_dev->dev, "No signal; needn't start frame\n"); return 0; } @@ -855,7 +883,7 @@ static int phytium_jpeg_buf_prepare(struct vb2_buffer *vb) static inline struct phytium_jpeg_buffer * phytium_vb2buf_to_dstbuf(struct vb2_v4l2_buffer *buf) { - return container_of(buf, struct phytium_jpeg_buffer, vb); + return container_of(buf, struct phytium_jpeg_buffer, vb); } static void phytium_jpeg_buf_queue(struct vb2_buffer *vb) @@ -914,7 +942,12 @@ static irqreturn_t phytium_jpeg_irq(int irq, void *arg) u32 frame_size; if (test_bit(VIDEO_POWEROFF, &jpeg_dev->status)) { - dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff\n"); + dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff 0x%x\n", + phytium_jpeg_read(jpeg_dev, INT_STATUS_CTRL_REG)); + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* clear all interruption of the hardware's buffers */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, INT_JPEG_ENCODE_COMPLETE, 1); return IRQ_HANDLED; } @@ -1065,7 +1098,7 @@ static irqreturn_t phytium_jpeg_timer31_irq(int irq, void *arg) /* clear timer interrupt status */ writel(0x8, jpeg_dev->timer31_addr + 0x2c); - /* clear JPEG Engine's poweroff status */ + /* clear JPEG Engine's poweroff status */ clear_bit(VIDEO_POWEROFF, &jpeg_dev->status); dev_info(jpeg_dev->dev, "timer31 set jpeg status 0x%lx\n", jpeg_dev->status); @@ -1105,13 +1138,22 @@ static irqreturn_t phytium_jpeg_timer30_irq(int irq, void *arg) struct phytium_jpeg_dev *jpeg_dev = arg; struct arm_smccc_res res; + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + /* disable timer interrupt */ writel(0, jpeg_dev->timer30_addr); /* clear timer interrupt status */ writel(0x8, jpeg_dev->timer30_addr + 0x2c); - /* Disable interruption */ - phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* disable all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); + udelay(5); + /* clear all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + /* disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, 0, 0); /* call SE to poweroff JPEG Engine */ arm_smccc_smc(0xc300fff4, 0x9, 0x2, 0x80000020, 0, 0, 0, 0, &res); @@ -1201,12 +1243,45 @@ static int phytium_jpeg_init(struct phytium_jpeg_dev *jpeg_dev) } +/* The function is provided for user space adjusts the sampling mode. */ +static int phytium_jpeg_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct phytium_jpeg_dev *jpeg_dev = container_of(ctrl->handler, + struct phytium_jpeg_dev, + ctrl_handler); + if (ctrl->id != V4L2_CID_JPEG_CHROMA_SUBSAMPLING) { + return -EINVAL; + } + + switch (ctrl->val) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + strncpy(yuv_mode_str, "yuv420", sizeof(yuv_mode_str)); + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + strncpy(yuv_mode_str, "yuv422", sizeof(yuv_mode_str)); + break; + default: + strncpy(yuv_mode_str, "yuv444", sizeof(yuv_mode_str)); + } + phytium_jpeg_set_yuv_mode(jpeg_dev); + dev_info(jpeg_dev->dev, "current sample mode is %s\n", yuv_mode_str); + return 0; +} + +static const struct v4l2_ctrl_ops phytium_jpeg_ctrl_ops = { + .s_ctrl = phytium_jpeg_set_ctrl, +}; + + static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) { struct v4l2_device *v4l2_dev = &jpeg_dev->v4l2_dev; struct vb2_queue *dst_vq = &jpeg_dev->queue; struct video_device *vdev = &jpeg_dev->vdev; + const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) | + BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_422) | + BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420)); int ret; jpeg_dev->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; @@ -1220,8 +1295,19 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) dev_err(jpeg_dev->dev, "Failed to register v4l2 device\n"); return ret; } - /* Register how many v4l2 controls to a handler */ + v4l2_ctrl_handler_init(&jpeg_dev->ctrl_handler, 1); + v4l2_ctrl_new_std_menu(&jpeg_dev->ctrl_handler, &phytium_jpeg_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, + V4L2_JPEG_CHROMA_SUBSAMPLING_444); + if (jpeg_dev->ctrl_handler.error) { + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); + dev_err(jpeg_dev->dev, "Failed to init v4l2 controls:%d\n", + jpeg_dev->ctrl_handler.error); + goto err_v4l2_register; + } + v4l2_dev->ctrl_handler = &jpeg_dev->ctrl_handler; dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; dst_vq->dev = v4l2_dev->dev; @@ -1234,6 +1320,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) dst_vq->min_buffers_needed = CAPTURE_BUF_NUMBER; ret = vb2_queue_init(dst_vq); if (ret) { + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); dev_err(jpeg_dev->dev, "Failed to init vb2 queue\n"); goto err_v4l2_register; } @@ -1263,7 +1350,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) err_video_register: vb2_queue_release(dst_vq); - + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); err_v4l2_register: v4l2_device_unregister(v4l2_dev); return ret; @@ -1352,10 +1439,14 @@ static int phytium_jpeg_remove(struct platform_device *pdev) phytium_jpeg_off(jpeg_dev); + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, 0); + video_unregister_device(&jpeg_dev->vdev); vb2_queue_release(&jpeg_dev->queue); + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); + v4l2_device_unregister(v4l2_dev); of_reserved_mem_device_release(dev); diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h index 5cb98e0846..7c9d088aa1 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h @@ -45,7 +45,7 @@ #define MAX_PIXEL_CLOCK (1920 * 1080 * 60) /* 1920 x 1080 x 60Hz */ #define SOURCE_RESOLUTION_DETECT_TIMEOUT msecs_to_jiffies(500) -#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(0) +#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(250) #define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) #define STOP_TIMEOUT msecs_to_jiffies(1000) @@ -128,6 +128,7 @@ struct phytium_jpeg_dev { unsigned int frame_rate; void __iomem *timer30_addr; void __iomem *timer31_addr; + struct v4l2_ctrl_handler ctrl_handler; }; struct phytium_jpeg_config { -- Gitee From 68ed35d4af307dae4bed80717547174779571641 Mon Sep 17 00:00:00 2001 From: hujun Date: Sat, 11 May 2024 16:05:15 +0800 Subject: [PATCH 14/41] scmi-hwmon: Multiply the sensor value by 1000 Signed-off-by: hujun --- drivers/hwmon/scmi-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 91976b6ca3..6e07b44aff 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -30,7 +30,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, sensor = *(scmi_sensors->info[type] + channel); ret = h->sensor_ops->reading_get(h, sensor->id, false, &value); if (!ret) - *val = value; + *val = value * 1000; return ret; } -- Gitee From bac13d504522df8d67d61f9426f2b147c6801388 Mon Sep 17 00:00:00 2001 From: hujun Date: Mon, 3 Jun 2024 15:13:56 +0800 Subject: [PATCH 15/41] pwm-phytium:Added cmpmod and channel independent configuretion dtsi add 'pwm-cells' for referencing eg: &tacho0 { ... pwms = <&pwm0 0 200000 0> ... } Signed-off-by: hujun --- .../devicetree/bindings/pwm/pwm-phytium.txt | 5 ++- arch/arm64/boot/dts/phytium/pe2201.dtsi | 4 ++ arch/arm64/boot/dts/phytium/pe220x.dtsi | 4 ++ drivers/pwm/pwm-phytium.c | 38 +++++++++++++------ 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm-phytium.txt b/Documentation/devicetree/bindings/pwm/pwm-phytium.txt index 0285e95a37..6b5bec3524 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-phytium.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-phytium.txt @@ -5,9 +5,10 @@ Required properties: - reg: physical base address and length of the controller's registers - interrupts: interrupt for the pwm controller - clocks : clock specifiers for both ipg and per clocks. -- phytium,db: One or two to describe dead-band configurations. +- phytium,db: One or two to describe dead-band configurations. "cntmod" indicates the counter mode (0 for modulo, 1 for up-and-down). "dutymod" indicdates which duty to compare with (0 for PMW_CCR, 1 for FIFO). + "cmpmod" indicdates compare operating configure, from 0 to 7. "div" selects the clock divider value, from 0 to 1023. "updbcly" selects the rising edge delay cycles. "dbpolarity" selects the polarity for dead-band. @@ -19,5 +20,5 @@ pwm0: pwm@2804a000 { reg= <0x0 0x2804a000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_48mhz>; - phytium,db = <0 0 0 1000 0>; + phytium,db = <0 0 3 0 1000 0>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index 5cedfa7c8c..56e74239b8 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -278,6 +278,7 @@ pwm4: pwm@2804e000 { reg = <0x0 0x2804e000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -286,6 +287,7 @@ pwm5: pwm@2804f000 { reg = <0x0 0x2804f000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -294,6 +296,7 @@ pwm6: pwm@28050000 { reg = <0x0 0x28050000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -302,6 +305,7 @@ pwm7: pwm@28051000 { reg = <0x0 0x28051000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index 49f251b745..fca212bf27 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -538,6 +538,7 @@ pwm0: pwm@2804a000 { reg = <0x0 0x2804a000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -546,6 +547,7 @@ pwm1: pwm@2804b000 { reg = <0x0 0x2804b000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -554,6 +556,7 @@ pwm2: pwm@2804c000 { reg = <0x0 0x2804c000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -562,6 +565,7 @@ pwm3: pwm@2804d000 { reg = <0x0 0x2804d000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; diff --git a/drivers/pwm/pwm-phytium.c b/drivers/pwm/pwm-phytium.c index 1bc02ff97d..23d347d4ca 100644 --- a/drivers/pwm/pwm-phytium.c +++ b/drivers/pwm/pwm-phytium.c @@ -42,6 +42,8 @@ #define PWM_UPDBCLY_MASK 0x3ff #define PWM_DWDBCLY_MASK 0xffc00 #define PWM_DB_POLARITY_MASK 0xc +#define PWM_CMPMOD_SHIFT 0x4 +#define PWM_CMPMOD_MASK 0x7 #define PWM_N(x) ((0x400)*(x)) #define MAX_PARAMETER 2 @@ -50,6 +52,7 @@ struct phytium_pwm_state { int rst; int cntmod; int dutymod; + unsigned int cmpmod; unsigned int div; int db_rst; unsigned int updbcly; @@ -60,6 +63,7 @@ struct phytium_pwm_state { struct phytium_pwm_param { int cntmod; int dutymod; + unsigned int cmpmod; unsigned int div; unsigned int updbcly; unsigned int dwdbcly; @@ -151,6 +155,18 @@ static void pwm_phytium_dutymod(struct pwm_chip *chip, int dutymod, int n) writel(reg, our_chip->base + PWM_N(n) + REG_PWMCTRL); } +static void pwm_phytium_set_cmpmod(struct pwm_chip *chip, unsigned int cmpmod, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_PWMCTRL); + cmpmod = cmpmod & PWM_CMPMOD_MASK; + reg &= ~ (PWM_CMPMOD_MASK << PWM_CMPMOD_SHIFT); + reg |= cmpmod << PWM_CMPMOD_SHIFT; + writel(reg, our_chip->base + PWM_N(n) + REG_PWMCTRL); +} + static void pwm_phytium_set_div(struct pwm_chip *chip, unsigned int div, int n) { struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); @@ -179,15 +195,13 @@ static void pwm_phytium_set_tmode(struct pwm_chip *chip, int tmode, int n) static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodns, int n) { struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); - u32 reg; - int div = our_chip->state.div; + int div = our_chip->parameter[n].div; u64 cycles; cycles = clk_get_rate(our_chip->base_clk); cycles *= (periodns / (div + 1)); do_div(cycles, NSEC_PER_SEC); - reg = readl(our_chip->base + PWM_N(n) + REG_TPERIOD); cycles = cycles & PWM_PERIOD_MASK; our_chip->state_pm[n].period = cycles; @@ -197,8 +211,7 @@ static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodn static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n) { struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); - u32 reg; - int div = our_chip->state.div; + int div = our_chip->parameter[n].div; u64 max_duty = our_chip->state_pm[n].period; u64 cycles; @@ -206,7 +219,6 @@ static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n cycles *= (duty / (div + 1)); do_div(cycles, NSEC_PER_SEC); - reg = readl(our_chip->base + PWM_N(n) + REG_PWMCCR); cycles = (max_duty - cycles) & PWM_DUTY_MASK; our_chip->state_pm[n].duty_cycle = cycles; @@ -268,9 +280,10 @@ static int pwm_phytium_init(struct pwm_chip *chip, struct pwm_device *pwm, int n writel(PWM_CTRL_INIT, our_chip->base + PWM_N(n) + REG_PWMCTRL); - pwm_phytium_dutymod(chip, our_chip->state.dutymod, n); - pwm_phytium_set_div(chip, our_chip->state.div, n); - pwm_phytium_set_tmode(chip, our_chip->state.cntmod, n); + pwm_phytium_dutymod(chip, our_chip->parameter[n].dutymod, n); + pwm_phytium_set_div(chip, our_chip->parameter[n].div, n); + pwm_phytium_set_tmode(chip, our_chip->parameter[n].cntmod, n); + pwm_phytium_set_cmpmod(chip, our_chip->parameter[n].cmpmod, n); return 0; } @@ -301,10 +314,11 @@ static int pwm_phytium_set_polarity(struct pwm_chip *chip, enum pwm_polarity pol if (polarity == PWM_POLARITY_INVERSED) { value &= 0xffffff0f; - value |= 0x30; + value |= (our_chip->parameter[n].cmpmod - 1) + << PWM_CMPMOD_SHIFT; } else if (polarity == PWM_POLARITY_NORMAL) { value &= 0xffffff0f; - value |= 0x40; + value |= our_chip->parameter[n].cmpmod << PWM_CMPMOD_SHIFT; } our_chip->state_pm[n].polarity = polarity; @@ -387,6 +401,7 @@ static int phytium_pwm_set_parameter(struct phytium_pwm_chip *priv) priv->state.cntmod = priv->parameter[i].cntmod; priv->state.dutymod = priv->parameter[i].dutymod; + priv->state.cmpmod = priv->parameter[i].cmpmod; priv->state.div = priv->parameter[i].div; priv->state.updbcly = priv->parameter[i].updbcly; priv->state.dwdbcly = priv->parameter[i].dwdbcly; @@ -418,6 +433,7 @@ static int pwm_phytium_probe_parameter(struct phytium_pwm_chip *priv, for (i = 0; i < priv->num_parameters; i++) { if (priv->parameter[i].cntmod > 1 || priv->parameter[i].dutymod > 1 || + priv->parameter[i].cmpmod > 7 || priv->parameter[i].div > 4096 || priv->parameter[i].dbpolarity > 3) return -EINVAL; -- Gitee From aae77809a067d9ecca32ad8286acc53b8a2d2cb9 Mon Sep 17 00:00:00 2001 From: hujun Date: Mon, 3 Jun 2024 15:42:08 +0800 Subject: [PATCH 16/41] tacho-phytium:Added pwm configuration support use in dts: &tacho0 { ... pwms = <&pwm0 0 200000 0> ... } Signed-off-by: hujun --- drivers/hwmon/tacho-phytium.c | 124 ++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/drivers/hwmon/tacho-phytium.c b/drivers/hwmon/tacho-phytium.c index a2863f6e81..ce1bfc8697 100644 --- a/drivers/hwmon/tacho-phytium.c +++ b/drivers/hwmon/tacho-phytium.c @@ -18,6 +18,7 @@ #include #include #include +#include #define TIMER_CTRL_REG 0x00 #define TIMER_CTRL_MODE_SHIFT 0//0:1 @@ -55,6 +56,8 @@ #define TIMER_INT_CLR_MASK GENMASK(5, 0) +#define MAX_PWM 255 + enum tacho_modes { tacho_mode = 1, capture_mode, @@ -66,9 +69,17 @@ falling_edge, double_edge, }; +struct pwm_data { + struct pwm_device *pwm; + struct mutex pwm_lock; + u8 pwm_value; + bool pwm_present; +}; + struct phytium_tacho { struct device *dev; struct device *hwmon; + struct pwm_data pwmdata; void __iomem *base; struct clk *clk; u32 freq; @@ -156,6 +167,77 @@ static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", rpm); } +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_tacho *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", priv->pwmdata.pwm_value); +} + +static int __set_pwm(struct phytium_tacho *priv, unsigned long pwm) +{ + unsigned long period; + int ret = 0; + struct pwm_state state = {}; + mutex_lock(&priv->pwmdata.pwm_lock); + + pwm_init_state(priv->pwmdata.pwm, &state); + period = priv->pwmdata.pwm->args.period; + state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + state.enabled = pwm ? true : false; + ret = pwm_apply_state(priv->pwmdata.pwm, &state); + mutex_unlock(&priv->pwmdata.pwm_lock); + return ret; +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct phytium_tacho *priv = dev_get_drvdata(dev); + long fan_ctrl; + + ret = kstrtol(buf, 10, &fan_ctrl); + if (ret != 0) + return ret; + + if (fan_ctrl < 0 || fan_ctrl > MAX_PWM) + return -EINVAL; + + if (priv->pwmdata.pwm_value == fan_ctrl) + return count; + + ret = __set_pwm(priv, fan_ctrl); + if (!ret) + priv->pwmdata.pwm_value = fan_ctrl; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1, 0644, show_pwm, set_pwm, 0); + +static struct attribute *pwm_attrs[] = { + &sensor_dev_attr_pwm1.dev_attr.attr, + NULL, +}; + +static umode_t pwm_is_visible(struct kobject *kobj, struct attribute *a, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct phytium_tacho *priv = dev_get_drvdata(dev); + + if (!priv->pwmdata.pwm_present) + return 0; + return a->mode; +} + +static const struct attribute_group pwm_group = { + .attrs = pwm_attrs, + .is_visible = pwm_is_visible, +}; + static SENSOR_DEVICE_ATTR(fan_input, 0444, show_rpm, NULL, 0); @@ -177,6 +259,7 @@ static const struct attribute_group tacho_group = { }; static const struct attribute_group *tacho_groups[] = { + &pwm_group, &tacho_group, NULL }; @@ -268,6 +351,45 @@ static int phytium_tacho_get_debounce(struct phytium_tacho *tacho) return 0; } +static int phytium_tacho_create_pwm_port(struct phytium_tacho *tacho) +{ + struct pwm_state state = {}; + int ret; + tacho->pwmdata.pwm_present = true; + tacho->pwmdata.pwm_value = MAX_PWM; + + /* Set duty cycle to maximum allowed and enable PWM output */ + pwm_init_state(tacho->pwmdata.pwm, &state); + state.duty_cycle = tacho->pwmdata.pwm->args.period - 1; + state.enabled = true; + + ret = pwm_apply_state(tacho->pwmdata.pwm, &state); + if (ret) + dev_err(tacho->dev, "Failed to configure PWM\n"); + return ret; +} + +static void phytium_tacho_create_pwm(struct phytium_tacho *tacho) +{ + u32 ret,len; + struct device_node *nc = tacho->dev->of_node; + + /* support without pwms property */ + if (of_find_property(nc, "pwms", &len)) { + tacho->pwmdata.pwm = devm_of_pwm_get(tacho->dev, nc, NULL); + if (IS_ERR(tacho->pwmdata.pwm)) { + dev_warn(tacho->dev, "Could not get PWM\n"); + } else { + ret = phytium_tacho_create_pwm_port(tacho); + if (ret) + dev_warn(tacho->dev, + "Could not creat PWM (%x)\n", ret); + } + } else { + dev_info(tacho->dev, "No PWM config\n"); + } +} + static int phytium_tacho_get_edges_per_revolution(struct phytium_tacho *tacho) { u32 value; @@ -332,6 +454,8 @@ static int phytium_tacho_probe(struct platform_device *pdev) return ret; } + phytium_tacho_create_pwm(tacho); + phytium_tacho_get_of_data(tacho); phytium_tacho_init(tacho); -- Gitee From 68985424a03a1989329aad6264920ec224f8c299 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Thu, 25 Jul 2024 15:07:23 +0800 Subject: [PATCH 17/41] i2c:Fix the return length of smbus block read command This patch fix the return length of smbus block read command. When use smbus block read command with pec, the return length is 2 lager than the data length,when use this command without pec,the return length is 1 lager than the data length. Signed-off-by: Wu Jinyong --- drivers/i2c/busses/i2c-phytium-mix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/i2c/busses/i2c-phytium-mix.c b/drivers/i2c/busses/i2c-phytium-mix.c index 4bc3c18366..50d2c0f3ad 100644 --- a/drivers/i2c/busses/i2c-phytium-mix.c +++ b/drivers/i2c/busses/i2c-phytium-mix.c @@ -302,6 +302,9 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) dev_err(dev->dev, "The value %d is out of the SMBus range [1~32]\n", *buf); *buf = 0; dev->msg_err = -EINVAL; + } else { + msgs[dev->msg_read_idx].len = *buf + + ((flags & I2C_CLIENT_PEC) ? 2 : 1); } } buf++; -- Gitee From 0f74299649c687de5c382d2246fb6262088bffe0 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Fri, 2 Aug 2024 17:19:03 +0800 Subject: [PATCH 18/41] i2c: Fixed issue with smbus block reading The I2C master driver has the issue of smbus block read (i2cget -f -y s) errors. This patch fixes this bug and modify the pec length. Signed-off-by: Wu Jinyong --- drivers/i2c/busses/i2c-phytium-master.c | 30 ++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-phytium-master.c b/drivers/i2c/busses/i2c-phytium-master.c index 8d8a843df4..18607c491d 100644 --- a/drivers/i2c/busses/i2c-phytium-master.c +++ b/drivers/i2c/busses/i2c-phytium-master.c @@ -116,6 +116,9 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) break; } + if (flags & I2C_M_RECV_LEN) + msgs[dev->msg_write_idx].len = I2C_SMBUS_BLOCK_MAX + 1; + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { /* new i2c_msg */ buf = msgs[dev->msg_write_idx].buf; @@ -132,8 +135,8 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { u32 cmd = 0; - if (dev->msg_write_idx == dev->msgs_num - 1 && - buf_len == 1 && !(flags & I2C_M_RECV_LEN)) + if (dev->msg_write_idx == dev->msgs_num - 1 + && buf_len == 1) cmd |= BIT(9); if (need_restart) { cmd |= BIT(10); @@ -163,7 +166,7 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop * the transaction here. */ - if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + if (buf_len > 0) { /* more bytes to be written */ dev->status |= STATUS_WRITE_IN_PROGRESS; break; @@ -190,11 +193,14 @@ static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) * Adjust the buffer length and mask the flag * after receiving the first byte. */ + if (len > I2C_SMBUS_BLOCK_MAX) + len = 0; len += (flags & I2C_CLIENT_PEC) ? 2 : 1; dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); - msgs[dev->msg_read_idx].len = len; - msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; - + if (dev->tx_buf_len == 0) { + dev->tx_buf_len = 1; + len = dev->rx_outstanding + 1; + } return len; } @@ -225,9 +231,17 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) *buf = phytium_readl(dev, IC_DATA_CMD); /* Ensure length byte is a valid value */ - if (flags & I2C_M_RECV_LEN && - *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; len = i2c_phytium_recv_len(dev, *buf); + if (*buf > I2C_SMBUS_BLOCK_MAX || *buf == 0) { + dev_err(dev->dev, "Value %d is out of range[1~32]\n", *buf); + *buf = 0; + dev->msg_err = -EINVAL; + } else { + msgs[dev->msg_read_idx].len = *buf + + ((flags & I2C_CLIENT_PEC) ? 2 : 1); + } } buf++; dev->rx_outstanding--; -- Gitee From 4318511e88a8781d94bd8872871fca91bda62d32 Mon Sep 17 00:00:00 2001 From: fengjun Date: Fri, 18 Oct 2024 20:05:44 +0800 Subject: [PATCH 19/41] reset-phytium: Add support for resetting phytium-i2c controller This patch adds support for resetting phytium-i2c controller when it works badly. Signed-off-by: fengjun Signed-off-by: Wu Jinyong Change-Id: I2958d7dcd3dc0c6afbf651c85b86029763a34138 --- arch/arm64/boot/dts/phytium/pe2201.dtsi | 26 +++++ drivers/i2c/busses/i2c-phytium-common.c | 48 +-------- drivers/reset/Kconfig | 9 ++ drivers/reset/Makefile | 1 + drivers/reset/reset-phytium.c | 131 ++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 46 deletions(-) create mode 100644 drivers/reset/reset-phytium.c diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index 56e74239b8..ea26cbd986 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -28,12 +28,20 @@ cpu0: cpu@0 { }; &soc { + reset: reset@2807e000 { + compatible = "phytium,reset"; + reg = <0x0 0x2807e000 0x0 0x10>; + #reset-cells = <1>; + status = "okay"; + }; + i2c0: i2c@28011000 { compatible = "phytium,i2c"; reg = <0x0 0x28011000 0x0 0x1000>; interrupts = ; interrupt-names = "smbus_alert"; clocks = <&sysclk_50mhz>; + resets = <&reset 0>; status = "disabled"; }; @@ -43,6 +51,7 @@ i2c1: i2c@28012000 { interrupts = ; interrupt-names = "smbus_alert"; clocks = <&sysclk_50mhz>; + resets = <&reset 1>; status = "disabled"; }; @@ -51,6 +60,7 @@ i2c2: i2c@28013000 { reg = <0x0 0x28013000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + resets = <&reset 2>; status = "disabled"; }; @@ -61,6 +71,7 @@ mio0: i2c@28014000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 3>; status = "disabled"; }; @@ -71,6 +82,7 @@ mio1: i2c@28016000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 4>; status = "disabled"; }; @@ -81,6 +93,7 @@ mio2: i2c@28018000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 5>; status = "disabled"; }; @@ -91,6 +104,7 @@ mio3: i2c@2801a000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 6>; status = "disabled"; }; @@ -101,6 +115,7 @@ mio4: i2c@2801c000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 7>; status = "disabled"; }; @@ -111,6 +126,7 @@ mio5: i2c@2801e000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 8>; status = "disabled"; }; @@ -121,6 +137,7 @@ mio6: i2c@28020000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 9>; status = "disabled"; }; @@ -131,6 +148,7 @@ mio7: i2c@28022000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 10>; status = "disabled"; }; @@ -141,6 +159,7 @@ mio8: i2c@28024000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 11>; status = "disabled"; }; @@ -151,6 +170,7 @@ mio9: i2c@28026000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 12>; status = "disabled"; }; @@ -161,6 +181,7 @@ mio10: i2c@28028000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 13>; status = "disabled"; }; @@ -171,6 +192,7 @@ mio11: i2c@2802a000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 14>; status = "disabled"; }; @@ -181,6 +203,7 @@ mio12: i2c@2802c000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 15>; status = "disabled"; }; @@ -191,6 +214,7 @@ mio13: i2c@2802e000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 16>; status = "disabled"; }; @@ -201,6 +225,7 @@ mio14: i2c@28030000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 17>; status = "disabled"; }; @@ -211,6 +236,7 @@ mio15: i2c@28032000 { clocks = <&sysclk_50mhz>; #address-cells = <1>; #size-cells = <0>; + resets = <&reset 18>; status = "disabled"; }; diff --git a/drivers/i2c/busses/i2c-phytium-common.c b/drivers/i2c/busses/i2c-phytium-common.c index 1c890c2148..07ca96a7c3 100644 --- a/drivers/i2c/busses/i2c-phytium-common.c +++ b/drivers/i2c/busses/i2c-phytium-common.c @@ -21,70 +21,26 @@ #include #include #include +#include #include "i2c-phytium-core.h" extern int i2c_phytium_init_master(struct phytium_i2c_dev *dev); extern int i2c_phytium_init_slave(struct phytium_i2c_dev *dev); -struct i2c_pair { - u8 index; - u64 i2c_base_addr; -}; - -struct i2c_pair ipr[] = { - { 0, 0x28014000 }, { 1, 0x28016000 }, { 2, 0x28018000 }, - { 3, 0x2801a000 }, { 4, 0x2801c000 }, { 5, 0x2801e000 }, - { 6, 0x28020000 }, { 7, 0x28022000 }, { 8, 0x28024000 }, - { 9, 0x28026000 }, { 0xa, 0x28028000 }, { 0xb, 0x2802a000 }, - { 0xc, 0x2802c000 }, { 0xd, 0x2802e000 }, { 0xe, 0x28030000 }, - { 0xf, 0x28032000 }, -}; -/* - * Add for i2c recover when controller status is wrong - */ -int get_i2c_recover_index(u64 resource_addr) -{ - int index; - for (index = 0; index < sizeof(ipr) / sizeof(struct i2c_pair); index++) - if (resource_addr == ipr[index].i2c_base_addr) - return index; - return -1; -} - int i2c_recover_controller(struct phytium_i2c_dev *dev) { int index; - u32 reset_reg_val; - u32 reg_val; unsigned long flags; - void __iomem *i2c_base; spin_lock_irqsave(&dev->i2c_lock, flags); + reset_control_reset(dev->rst); - index = get_i2c_recover_index(dev->i2c_resource_addr); - if (index < 0) { - spin_unlock_irqrestore(&dev->i2c_lock, flags); - return -EFAULT; - } - i2c_base = ioremap(I2C_RESET_BASE_ADDR, REG_LEN); - if (!i2c_base) { - spin_unlock_irqrestore(&dev->i2c_lock, flags); - return -EFAULT; - } - - reg_val = readl_relaxed(i2c_base + I2C_RESET_OFFSET); - reset_reg_val = reg_val; - reg_val &= ~(1 << (SHIFT_BIT + index)); - writel_relaxed(reg_val, i2c_base + I2C_RESET_OFFSET); - writel_relaxed(reset_reg_val, i2c_base + I2C_RESET_OFFSET); #if IS_ENABLED(CONFIG_I2C_SLAVE) dev->slave_state = SLAVE_STATE_IDLE; dev->status = STATUS_IDLE; if (dev->slave) phytium_writel(dev, dev->slave->addr, IC_SAR); #endif - iounmap(i2c_base); - i2c_base = NULL; spin_unlock_irqrestore(&dev->i2c_lock, flags); return 0; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 13d28fdbdb..661e205dbf 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -144,6 +144,15 @@ config RESET_TI_SYSCON you wish to use the reset framework for such memory-mapped devices, say Y here. Otherwise, say N. +config RESET_PHYTIUM + bool "PHYTIUM Reset Driver" + default ARCH_iPHYTIUM + help + This enables the reset driver support for phytium devices with + memory-mapped reset registers as part of a syscon device node. If + you wish to use the reset framework for such memory-mapped devices, + say Y here. Otherwise, say N. + config RESET_UNIPHIER tristate "Reset controller driver for UniPhier SoCs" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 4243c38228..8417671c0e 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o +obj-$(CONFIG_RESET_PHYTIUM) += reset-phytium.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o diff --git a/drivers/reset/reset-phytium.c b/drivers/reset/reset-phytium.c new file mode 100644 index 0000000000..c77be1be75 --- /dev/null +++ b/drivers/reset/reset-phytium.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Simple Reset Controller Driver + * + * Copyright (C) 2017 Pengutronix, Philipp Zabel + * + * Based on Allwinner SoCs Reset Controller driver + * + * Copyright 2013 Maxime Ripard + * + * Maxime Ripard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTIUM_RESET_MAX_CNTS (19) +#define PHYTIUM_I2C_RESET_ID0 (3) +#define PHYTIUM_RESET_OFFSET_0 (0) +#define PHYTIUM_RESET_OFFSET_1 (4) + +struct reset_phytium_dev { + void __iomem *base; + struct device *dev; + struct reset_controller_dev rcdev; +}; + +struct reset_reg_info { + u32 reg_offset; + u32 data; +}; +static struct reset_reg_info reset_mng[PHYTIUM_RESET_MAX_CNTS] = { + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(28)}, + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(29)}, + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(30)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(16)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(17)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(18)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(19)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(20)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(21)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(22)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(23)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(24)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(25)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(26)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(27)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(28)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(29)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(30)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(31)} + }; + +static inline struct reset_phytium_dev * +to_reset_phytium_data(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct reset_phytium_dev, rcdev); +} + +static int reset_phytium_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_phytium_dev *rdev = to_reset_phytium_data(rcdev); + u32 reg_val, reset_reg_val; + + if (id >= PHYTIUM_RESET_MAX_CNTS) { + dev_err(rdev->dev, "The reset id is out of range %ld\n",id); + return -EINVAL; + } + + reg_val = readl_relaxed(rdev->base + reset_mng[id].reg_offset); + reset_reg_val = reg_val; + reg_val &= ~reset_mng[id].data; + writel_relaxed(reg_val, rdev->base + reset_mng[id].reg_offset); + writel_relaxed(reset_reg_val, rdev->base + reset_mng[id].reg_offset); + + return 0; +} + +const struct reset_control_ops reset_phytium_ops = { + .reset = reset_phytium_reset, +}; +EXPORT_SYMBOL_GPL(reset_phytium_ops); + +static const struct of_device_id reset_phytium_dt_ids[] = { + { .compatible = "phytium,reset", }, + { /* sentinel */ }, +}; + +static int reset_phytium_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct reset_phytium_dev *rdev; + struct resource *res; + + rdev = devm_kzalloc(dev, sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rdev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(rdev->base)) + return PTR_ERR(rdev->base); + + rdev->rcdev.owner = THIS_MODULE; + rdev->rcdev.nr_resets = PHYTIUM_RESET_MAX_CNTS; + rdev->rcdev.ops = &reset_phytium_ops; + rdev->rcdev.of_node = dev->of_node; + rdev->dev = &pdev->dev; + + return devm_reset_controller_register(dev, &rdev->rcdev); +} + +static struct platform_driver reset_phytium_driver = { + .probe = reset_phytium_probe, + .driver = { + .name = "phytium-reset", + .of_match_table = reset_phytium_dt_ids, + }, +}; +builtin_platform_driver(reset_phytium_driver); +MODULE_DESCRIPTION("phytium reset"); +MODULE_LICENSE("GPL"); -- Gitee From f3e6b626b4812ebc2e1c77f968e14d3bf8e6fa3c Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Mon, 8 Jul 2024 10:39:18 +0800 Subject: [PATCH 20/41] kcs:phytium: Avoid OBF and IBF not being set right on pe220x BMC cards This patch reads ODR and writes IDR twice to avoid unessasery trouble on phytium pe220x BMC cards. Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I4f9e3e19ec311167e861adacf9124fb3ac75c9ed --- drivers/char/ipmi/kcs_bmc_phytium.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/ipmi/kcs_bmc_phytium.c b/drivers/char/ipmi/kcs_bmc_phytium.c index 38729de065..8b4c817083 100644 --- a/drivers/char/ipmi/kcs_bmc_phytium.c +++ b/drivers/char/ipmi/kcs_bmc_phytium.c @@ -95,6 +95,10 @@ static u8 phytium_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) rc = regmap_read(priv->map, reg, &val); WARN(rc != 0, "regmap_read() failed: %d\n", rc); + if (reg == LPC_IDR1 || reg == LPC_IDR2 || + reg == LPC_IDR3 || reg == LPC_IDR4) + rc = regmap_read(priv->map, reg, &val); + return rc == 0 ? (u8) val : 0; } @@ -103,6 +107,10 @@ static void phytium_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) struct phytium_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); int rc; + if (reg == LPC_ODR1 || reg == LPC_ODR2 || + reg == LPC_ODR3 || reg == LPC_ODR4) + regmap_write(priv->map, reg, data); + rc = regmap_write(priv->map, reg, data); WARN(rc != 0, "regmap_write() failed: %d\n", rc); } -- Gitee From a73bef8f73c00734e7fbb858ebc83f8cc6568469 Mon Sep 17 00:00:00 2001 From: Zhou Yulin Date: Fri, 31 Mar 2023 18:09:59 +0800 Subject: [PATCH 21/41] iio: adc: Slove rmmod NULL point problem This patch modify adc driver to match iio_device_register and iio_device_unregister to avoid rmmod NULL point problem. Signed-off-by: Zhou Yulin Signed-off-by: Wang Yinfeng Change-Id: Ibc4fcd3ad1e9d821d6c991ed41bc4dfbdc4b57eb --- drivers/iio/adc/phytium-adc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/phytium-adc.c b/drivers/iio/adc/phytium-adc.c index 093a47fd3d..fbadb52397 100644 --- a/drivers/iio/adc/phytium-adc.c +++ b/drivers/iio/adc/phytium-adc.c @@ -579,7 +579,6 @@ static int phytium_adc_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; - platform_set_drvdata(pdev, indio_dev); adc = iio_priv(indio_dev); adc->dev = dev; @@ -609,6 +608,8 @@ static int phytium_adc_probe(struct platform_device *pdev) indio_dev->channels = adc->data->channels; indio_dev->num_channels = adc->data->num_channels; + platform_set_drvdata(pdev, indio_dev); + ret = devm_request_threaded_irq(adc->dev, platform_get_irq(pdev, 0), NULL, phytium_adc_threaded_irq, IRQF_ONESHOT, dev_name(dev), adc); @@ -628,7 +629,7 @@ static int phytium_adc_probe(struct platform_device *pdev) return ret; } - return devm_iio_device_register(dev, indio_dev); + return iio_device_register(indio_dev); } static int phytium_adc_remove(struct platform_device *pdev) -- Gitee From dcb86e734cd2542b66c0b7feca5eeaa67249d445 Mon Sep 17 00:00:00 2001 From: Li Yuze Date: Tue, 13 Aug 2024 15:38:38 +0800 Subject: [PATCH 22/41] gpio: phytium: Modify GPIO driver to the kernel buildin module When this driver is used in ko module, it can't enable gic interrupts automatically after resuming from s4(Suppend from Disk).So modify GPIO Kconfig from tristate to bool. Signed-off-by: Li Yuze Signed-off-by: Wang Yinfeng Change-Id: I5f13519d267e730d197996f9bf212d8971e0dfe5 --- drivers/gpio/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index a6649370b9..03b0b78a01 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -81,7 +81,7 @@ config GPIO_GENERIC # This symbol is selected by both MMIO and PCI expanders config GPIO_PHYTIUM_CORE - tristate + bool # This symbol is selected by both I2C and SPI expanders config GPIO_MAX730X @@ -409,7 +409,7 @@ config GPIO_OMAP Say yes here to enable GPIO support for TI OMAP SoCs. config GPIO_PHYTIUM_PLAT - tristate "Phytium GPIO Platform support" + bool "Phytium GPIO Platform support" default y if ARCH_PHYTIUM depends on ARM64 select GPIO_PHYTIUM_CORE @@ -1332,7 +1332,7 @@ config GPIO_PCIE_IDIO_24 filters are deactivated by this driver. config GPIO_PHYTIUM_PCI - tristate "Phytium GPIO PCI support" + bool "Phytium GPIO PCI support" select GPIO_PHYTIUM_CORE select IRQ_DOMAIN select GENERIC_IRQ_CHIP -- Gitee From 2607c53411ea643c4f0d9216c9ec97941d4bc4d6 Mon Sep 17 00:00:00 2001 From: Peng Min Date: Fri, 26 Jul 2024 16:20:42 +0800 Subject: [PATCH 23/41] mtd: spi-nor: Add flash support for two new models Add flash support for two new models, their JEDEC ID are ef7018 and c86018. Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Change-Id: Ia060b97fe020f1b30f3ef4400cd67e1fe18aec0b --- drivers/mtd/spi-nor/spi-nor.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 2f70f3a96d..a6e1339f47 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1055,6 +1055,11 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "gd25lq128d", INFO(0xc86018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | @@ -1283,6 +1288,11 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, -- Gitee From 0a495801d0dedbf94985bbac056ed4020b0917b4 Mon Sep 17 00:00:00 2001 From: Jiakun Shuai Date: Tue, 29 Oct 2024 16:18:54 +0800 Subject: [PATCH 24/41] spi: phytium: Bugfix phytium SPI driver panic problem The root cause is that irq is triggered between request_irq and spi_master_set devdata, and fts has not been initialized yet. Signed-off-by: Liu Dalin Tested-by: Peng Min Signed-off-by: Wang Yinfeng Signed-off-by: Jiakun Shuai Change-Id: I06529316e16b728e812ce686f84bab2a59ada608 --- drivers/spi/spi-phytium.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-phytium.c b/drivers/spi/spi-phytium.c index f948a9cc17..d29eaa19cc 100644 --- a/drivers/spi/spi-phytium.c +++ b/drivers/spi/spi-phytium.c @@ -413,6 +413,8 @@ int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) fts->dma_addr = (dma_addr_t)(fts->paddr + DR); snprintf(fts->name, sizeof(fts->name), "phytium_spi%d", fts->bus_num); + spi_hw_init(dev, fts); + ret = request_irq(fts->irq, phytium_spi_irq, IRQF_SHARED, fts->name, master); if (ret < 0) { dev_err(dev, "can not get IRQ\n"); @@ -434,8 +436,6 @@ int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) master->flags = SPI_MASTER_GPIO_SS; master->cs_gpios = fts->cs; - spi_hw_init(dev, fts); - if (fts->dma_ops && fts->dma_ops->dma_init) { ret = fts->dma_ops->dma_init(dev, fts); if (ret) { @@ -447,6 +447,7 @@ int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) } spi_master_set_devdata(master, fts); + ret = devm_spi_register_master(dev, master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); -- Gitee From ad2252ccd60351732c8b9aa6c012787877510f28 Mon Sep 17 00:00:00 2001 From: Jiakun Shuai Date: Wed, 30 Oct 2024 14:55:18 +0800 Subject: [PATCH 25/41] qspi: phytium: Add indirect write mode Add indirect write mode for qspi controller driver, and the default value is indirect write. Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Signed-off-by: Jiakun Shuai Change-Id: I21c9784d9db355d937cd23450b614965b05a2fe7 --- drivers/mtd/spi-nor/phytium-quadspi.c | 94 ++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/phytium-quadspi.c b/drivers/mtd/spi-nor/phytium-quadspi.c index 815c51f8b5..344b707f9f 100644 --- a/drivers/mtd/spi-nor/phytium-quadspi.c +++ b/drivers/mtd/spi-nor/phytium-quadspi.c @@ -149,6 +149,8 @@ #define PHYTIUM_FMODE_MM 0x01 #define PHYTIUM_FMODE_IN 0x02 +#define WR_CFG_NODIRMAP_VALUE 0x5000000 + /* * the codes of the different commands */ @@ -689,6 +691,86 @@ static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, return len; } +static ssize_t phytium_qspi_nodirmap_write(struct spi_nor *nor, loff_t to, size_t len, + const u_char *buf) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = nor->program_opcode; + u32 addr = (u32)to; + int i; + u_char tmp[8] = {0}; + size_t mask = 0x03; + size_t mask_p = 0x07; + u8 len_p; + + if (addr & 0x03) { + dev_err(dev, "Addr not four-byte aligned!\n"); + return -EINVAL; + } + + cmd = cmd << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + cmd |= flash->clk_div & QSPI_CMD_PORT_SCK_SEL_MASK; + cmd |= 0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT; + + switch (nor->program_opcode) { + case CMD_PP: + case CMD_QPP: + cmd &= ~(0x1 << QSPI_CMD_PORT_SEL_SHIFT); + break; + case CMD_4PP: + case CMD_4QPP: + cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); + break; + default: + dev_err(qspi->dev, "Not support program command:%#x\n", + nor->erase_opcode); + return -EINVAL; + } + + for (i = 0; i < len/8; i++) { + phytium_qspi_write_enable(qspi, flash); + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + writel_relaxed(*(u32 *)(buf + 4 + 8 * i), + qspi->io_base + QSPI_HD_PORT_REG); + writel_relaxed(*(u32 *)(buf + 8 * i), + qspi->io_base + QSPI_LD_PORT_REG); + phytium_qspi_wait_cmd(qspi, flash); + phytium_qspi_write_disable(qspi, flash); + addr += 8; + } + + len_p = (u8)(len & mask_p); + if (len_p) { + phytium_qspi_write_enable(qspi, flash); + cmd &= ~(0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT); + cmd |= (len_p - 1) << QSPI_CMD_PORT_RW_NUM_SHIFT; + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + if ((len_p - 4) > 0) { + memcpy(tmp, buf + 4 + (len / 8) * 8, len_p - 4); + writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_HD_PORT_REG); + memcpy(tmp, buf + (len / 8) * 8, 4); + writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_LD_PORT_REG); + } else if (len_p == 4) { + memcpy(tmp, buf + (len / 8) * 8, 4); + writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_LD_PORT_REG); + } else { + memcpy(tmp, buf + (len / 8) * 8, len & mask); + writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_LD_PORT_REG); + } + } + + phytium_qspi_wait_cmd(qspi, flash); + + return len; +} + static int phytium_qspi_erase(struct spi_nor *nor, loff_t offs) { struct phytium_qspi_flash *flash = nor->priv; @@ -818,6 +900,7 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, struct phytium_qspi_flash *flash; struct mtd_info *mtd; int ret; + bool dirmap_write = false; of_property_read_u32(np, "reg", &cs_num); if (cs_num >= PHYTIUM_MAX_NORCHIP) @@ -863,6 +946,9 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, SNOR_HWCAPS_PP_4_4_4; } + if (of_property_read_bool(np, "dirmap-write")) + dirmap_write = true; + flash = &qspi->flash[cs_num]; flash->qspi = qspi; flash->cs = cs_num; @@ -876,7 +962,10 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, mtd = &flash->nor.mtd; flash->nor.read = phytium_qspi_read; - flash->nor.write = phytium_qspi_write; + if (dirmap_write) + flash->nor.write = phytium_qspi_write; + else + flash->nor.write = phytium_qspi_nodirmap_write; flash->nor.erase = phytium_qspi_erase; flash->nor.read_reg = phytium_qspi_read_reg; flash->nor.write_reg = phytium_qspi_write_reg; @@ -903,6 +992,9 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); writel_relaxed(0x05000000, qspi->io_base + QSPI_WR_CFG_REG); + if (!dirmap_write) + writel_relaxed(WR_CFG_NODIRMAP_VALUE, qspi->io_base + QSPI_WR_CFG_REG); + flash->read_mode = PHYTIUM_FMODE_MM; ret = mtd_device_register(mtd, NULL, 0); -- Gitee From 5f1d885f81747746d05920bac65f0e980a0a5a86 Mon Sep 17 00:00:00 2001 From: Jiakun Shuai Date: Tue, 29 Oct 2024 18:04:56 +0800 Subject: [PATCH 26/41] revert: net: macb: add support for high speed interface Revert this commit to update the function. The original log is down below. --------------------------------- This controller has separate MAC's and PCS'es for low and high speed paths. High speed PCS supports 100M, 1G, 2.5G, 5G and 10G through rate adaptation implementation. However, since it doesn't support auto negotiation, linux driver is modified to support USXGMII/5GBASER/2500BASEX. Signed-off-by: Jiakun Shuai --- drivers/net/ethernet/cadence/macb.h | 9 --- drivers/net/ethernet/cadence/macb_main.c | 84 +++--------------------- 2 files changed, 8 insertions(+), 85 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index bfdc374d1e..147d55badc 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -80,7 +80,6 @@ #define MACB_RBQPH 0x04D4 /* GEM register offsets. */ -#define GEM_NCR 0x0000 /* Network Control */ #define GEM_NCFGR 0x0004 /* Network Config */ #define GEM_USRIO 0x000c /* User IO */ #define GEM_DMACFG 0x0010 /* DMA Configuration */ @@ -262,12 +261,6 @@ #define MACB_SRTSM_OFFSET 15 #define MACB_OSSMODE_OFFSET 24 /* Enable One Step Synchro Mode */ #define MACB_OSSMODE_SIZE 1 -#define MACB_2PT5G_OFFSET 29 /* 2.5G operation selected */ -#define MACB_2PT5G_SIZE 1 - -/* GEM specific NCR bitfields. */ -#define GEM_ENABLE_HS_MAC_OFFSET 31 -#define GEM_ENABLE_HS_MAC_SIZE 1 /* Bitfields in NCFGR */ #define MACB_SPD_OFFSET 0 /* Speed */ @@ -546,8 +539,6 @@ #define GEM_RX_SCR_BYPASS_SIZE 1 #define GEM_TX_SCR_BYPASS_OFFSET 8 #define GEM_TX_SCR_BYPASS_SIZE 1 -#define GEM_RX_SYNC_RESET_OFFSET 2 -#define GEM_RX_SYNC_RESET_SIZE 1 #define GEM_TX_EN_OFFSET 1 #define GEM_TX_EN_SIZE 1 #define GEM_SIGNAL_OK_OFFSET 0 diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 558c12efe0..2d4a03240e 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -85,7 +85,6 @@ #define HS_SPEED_2500M 2 #define HS_SPEED_5000M 3 #define HS_SPEED_10000M 4 -#define MACB_SERDES_RATE_5G 0 #define MACB_SERDES_RATE_10G 1 #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) @@ -617,15 +616,7 @@ static int phytium_gem_sel_clk(struct macb *bp) gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ speed = HS_SPEED_10000M; } - } else if (bp->phy_interface == PHY_INTERFACE_MODE_5GBASER) { - if (bp->speed == SPEED_5000) { - gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ - gem_writel(bp, DIV_SEL0_LN, 0x8); /*0x1c08*/ - gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ - gem_writel(bp, PMA_XCVR_POWER_STATE, 0x0); /*0x1c10*/ - speed = HS_SPEED_5000M; - } - } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { if (bp->speed == SPEED_2500) { gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ @@ -641,9 +632,7 @@ static int phytium_gem_sel_clk(struct macb *bp) gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ speed = HS_SPEED_2500M; - } - } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { - if (bp->speed == SPEED_1000) { + } else if (bp->speed == SPEED_1000) { gem_writel(bp, DIV_SEL0_LN, 0x4); /*0x1c08*/ gem_writel(bp, DIV_SEL1_LN, 0x8); /*0x1c0c*/ gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ @@ -2561,39 +2550,8 @@ static void macb_configure_dma(struct macb *bp) static int macb_usx_pcs_config(struct macb *bp) { - u32 old_ctrl, ctrl; - u32 old_ncr, ncr; - - netdev_dbg(bp->dev, "macb usx pcs config"); - - ncr = macb_readl(bp, NCR); - ctrl = macb_or_gem_readl(bp, NCFGR); - old_ncr = ncr; - old_ctrl = ctrl; - - ncr &= ~(GEM_BIT(ENABLE_HS_MAC) | MACB_BIT(2PT5G)); - ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL) | MACB_BIT(SPD) | MACB_BIT(FD)); - if (macb_is_gem(bp)) - ctrl &= ~GEM_BIT(GBE); - - if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - ctrl |= GEM_BIT(PCSSEL) | GEM_BIT(SGMIIEN); - ncr |= MACB_BIT(2PT5G); - } else if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || - bp->phy_interface == PHY_INTERFACE_MODE_5GBASER) { - ctrl |= GEM_BIT(PCSSEL); - ncr |= GEM_BIT(ENABLE_HS_MAC); - } - - if (bp->duplex) - ctrl |= MACB_BIT(FD); - - /* Apply the new configuration, if any */ - if (old_ctrl ^ ctrl) - macb_or_gem_writel(bp, NCFGR, ctrl); - - if (old_ncr ^ ncr) - macb_or_gem_writel(bp, NCR, ncr); + gem_writel(bp, USX_CONTROL, gem_readl(bp, USX_CONTROL) | + GEM_BIT(SIGNAL_OK)); return 0; } @@ -2602,30 +2560,16 @@ static void macb_usx_pcs_link_up(struct macb *bp) { u32 config; - if (bp->phy_interface != PHY_INTERFACE_MODE_USXGMII && - bp->phy_interface != PHY_INTERFACE_MODE_5GBASER) - return; - - netdev_dbg(bp->dev, "macb usx pcs link up"); - config = gem_readl(bp, USX_CONTROL); if (bp->speed == SPEED_10000) { + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_10000M, + gem_readl(bp, HS_MAC_CONFIG))); config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_10G, config); config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_10000M, config); - } else if (bp->speed == SPEED_5000) { - config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_5G, config); - config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_5000M, config); } config &= ~(GEM_BIT(TX_SCR_BYPASS) | GEM_BIT(RX_SCR_BYPASS)); - config &= ~GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN); - config |= GEM_BIT(RX_SYNC_RESET); - - gem_writel(bp, USX_CONTROL, config); - - config &= ~(GEM_BIT(RX_SYNC_RESET)); - config |= GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN); - + config |= GEM_BIT(TX_EN); gem_writel(bp, USX_CONTROL, config); } @@ -2660,11 +2604,7 @@ static void macb_init_hw(struct macb *bp) if ((bp->caps & MACB_CAPS_JUMBO) && bp->jumbo_max_len) gem_writel(bp, JML, bp->jumbo_max_len); - if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || - bp->phy_interface == PHY_INTERFACE_MODE_5GBASER || - bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - if (bp->caps & MACB_CAPS_SEL_CLK_HW) - phytium_gem_sel_clk(bp); + if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { macb_usx_pcs_config(bp); if (bp->link) macb_usx_pcs_link_up(bp); @@ -4738,14 +4678,6 @@ static int macb_probe(struct platform_device *pdev) bp->link = 1; bp->duplex = 1; bp->speed = SPEED_10000; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_5GBASER) { - bp->link = 1; - bp->duplex = 1; - bp->speed = SPEED_5000; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - bp->link = 1; - bp->duplex = 1; - bp->speed = SPEED_2500; } /* IP specific init */ -- Gitee From 7bed00ddf61659552c5e83bb75c30843fc561170 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 27 Dec 2023 09:56:59 +0800 Subject: [PATCH 27/41] net:macb: Fix linux kernel crash when skb len is illegal This patch adds legality review for skb len and memory alloc. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I3d0660843ee687214c15d1c446e629435a63725c --- drivers/net/ethernet/cadence/macb_main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2d4a03240e..d7961cb1b5 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1242,7 +1242,6 @@ static void gem_rx_refill(struct macb_queue *queue) /* Make hw descriptor updates visible to CPU */ rmb(); - queue->rx_prepared_head++; desc = macb_rx_desc(queue, entry); if (!queue->rx_skbuff[entry]) { @@ -1263,6 +1262,7 @@ static void gem_rx_refill(struct macb_queue *queue) break; } + queue->rx_prepared_head++; queue->rx_skbuff[entry] = skb; if (entry == bp->rx_ring_size - 1) @@ -1352,6 +1352,15 @@ static int gem_rx(struct macb_queue *queue, int budget) queue->stats.rx_dropped++; break; } + + len = ctrl & bp->rx_frm_len_mask; + if (unlikely(len <= 0 || len > bp->rx_buffer_size)) { + netdev_err(bp->dev, "illegal skb len: %d\n", len); + bp->dev->stats.rx_dropped++; + queue->stats.rx_dropped++; + break; + } + skb = queue->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(bp->dev, @@ -1362,7 +1371,6 @@ static int gem_rx(struct macb_queue *queue, int budget) } /* now everything is ready for receiving packet */ queue->rx_skbuff[entry] = NULL; - len = ctrl & bp->rx_frm_len_mask; netdev_vdbg(bp->dev, "gem_rx %u (len %u)\n", entry, len); -- Gitee From 8ca8ab64c9ede7bbe207ad92b60e1e38b9e49e08 Mon Sep 17 00:00:00 2001 From: Jiakun Shuai Date: Thu, 31 Oct 2024 09:40:47 +0800 Subject: [PATCH 28/41] revert: net macb: add phy reset function Revert this commit to update phy reset function in future. Original commit ID: 9d7c5f3fc298e494d71ee4aac2ed237744c82781 Signed-off-by: Jiakun Shuai --- drivers/net/ethernet/cadence/macb.h | 8 - drivers/net/ethernet/cadence/macb_main.c | 295 ----------------------- 2 files changed, 303 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 147d55badc..fbdba68ecc 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -14,7 +14,6 @@ #include #include #include -#include #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) #define MACB_EXT_DESC @@ -1274,13 +1273,6 @@ struct macb { int tx_bd_rd_prefetch; u32 rx_intr_mask; - - void *phy_vbase_addr; - unsigned long crc_detect_period; /* default period: 0 ms; disable crc detect function */ - unsigned long nb_rx; /* CRC detection starts when more than nb_rx packets are received */ - unsigned long crc_err_percent; /* PHY RESET when crc error more than crc_err_percent */ - unsigned long phy_reset_times; - struct task_struct *poll_task; }; #ifdef CONFIG_MACB_USE_HWSTAMP diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index d7961cb1b5..bad8344440 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -38,9 +38,6 @@ #include #include #include -#include -#include -#include #include "macb.h" #define MACB_RX_BUFFER_SIZE 128 @@ -95,193 +92,6 @@ */ #define MACB_HALT_TIMEOUT 1230 -#define false 0 -#define true 1 -#define DEFAULT_SLEEP_S 1 -#define DEFAULT_CHECK_FCS_PERIOD_MS 2000 -#define DEFAULT_CHECK_FCS_RX_THRESHOLD 2 -#define DEFAULT_CHECK_FCS_PERCENT_THRESHOLD 1 - -#define PHY0_ADDR_BASE 0x32140000 -#define PHY0_ADDR_OFFSET 0x254 -#define PHY1_ADDR_BASE 0x32240000 -#define PHY1_ADDR_OFFSET 0x08c -#define PHY2_ADDR_BASE 0x32340000 -#define PHY2_ADDR_OFFSET 0x254 -#define PHY3_ADDR_BASE 0x32440000 -#define PHY3_ADDR_OFFSET 0x254 - -#define MACB0_ADDR_BASE 0x3200c000 -#define MACB1_ADDR_BASE 0x3200e000 -#define MACB2_ADDR_BASE 0x32010000 -#define MACB3_ADDR_BASE 0x32012000 - -#define MAP_SIZE 4096 - -static DEFINE_MUTEX(stats_mutex); -static int crc_detect_poll(void *args); - -static ssize_t -show_check_fcs_period(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - - return snprintf(buf, 10, "%lu\n", bp->crc_detect_period); -} - -static ssize_t -store_check_fcs_period(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int err = 0; - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - unsigned long crc_detect_period; - - if (0 != kstrtoul(buf, 0, &crc_detect_period)) - return -EINVAL; - - if (crc_detect_period < 0) - return -EINVAL; - bp->crc_detect_period = crc_detect_period; - - return err ? err : count; -} -static DEVICE_ATTR(check_fcs_period, S_IRUGO | S_IWUSR, show_check_fcs_period, store_check_fcs_period); - -static ssize_t -show_check_fcs_rx_threshold(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - - return snprintf(buf, 10, "%lu\n", bp->nb_rx); -} - -static ssize_t -store_check_fcs_rx_threshold(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int err = 0; - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - unsigned long nb_rx; - - if (0 != kstrtoul(buf, 0, &nb_rx)) - return -EINVAL; - - if (nb_rx < 0) - return -EINVAL; - bp->nb_rx = nb_rx; - - return err ? err : count; -} -static DEVICE_ATTR(check_fcs_rx_threshold, S_IRUGO | S_IWUSR, show_check_fcs_rx_threshold, store_check_fcs_rx_threshold); - -static ssize_t -show_check_fcs_percent_threshold(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - - return snprintf(buf, 10, "%lu\n", bp->crc_err_percent); -} - -static ssize_t -store_check_fcs_percent_threshold(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int err = 0; - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - unsigned long crc_err_percent; - - if (0 != kstrtoul(buf, 0, &crc_err_percent)) - return -EINVAL; - - if (crc_err_percent > 100 || crc_err_percent < 0) - return -EINVAL; - bp->crc_err_percent = crc_err_percent; - - return err ? err : count; -} -static DEVICE_ATTR(check_fcs_percent_threshold, S_IRUGO | S_IWUSR, show_check_fcs_percent_threshold, store_check_fcs_percent_threshold); - -static ssize_t -show_phy_reset_times(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); - struct macb *bp = netdev_priv(netdev); - - return snprintf(buf, 10, "%lu\n", bp->phy_reset_times); -} -static DEVICE_ATTR(phy_reset_times, S_IRUGO, show_phy_reset_times, NULL); - -static struct attribute *dev_attrs[] = { - &dev_attr_check_fcs_period.attr, - &dev_attr_check_fcs_rx_threshold.attr, - &dev_attr_check_fcs_percent_threshold.attr, - &dev_attr_phy_reset_times.attr, - NULL, -}; - -static const struct attribute_group dev_attr_grp = { - .attrs = dev_attrs, -}; - -static void -*get_mmap_addr(unsigned long phy_addr) -{ - void *virt_addr; - - virt_addr = ioremap(phy_addr, MAP_SIZE); - - if (!virt_addr) { - pr_err("Failed to map physical address to virtual address\n"); - return NULL; - } - - return virt_addr; -} - -static void -phy_base_mmap(struct net_device *dev) { - struct macb *bp = netdev_priv(dev); - void *base; - - switch (dev->base_addr) { - case MACB0_ADDR_BASE: - base = get_mmap_addr(PHY0_ADDR_BASE); - break; - case MACB1_ADDR_BASE: - base = get_mmap_addr(PHY1_ADDR_BASE); - break; - case MACB2_ADDR_BASE: - base = get_mmap_addr(PHY2_ADDR_BASE); - break; - case MACB3_ADDR_BASE: - base = get_mmap_addr(PHY3_ADDR_BASE); - break; - default: - break; - } - - bp->phy_vbase_addr = base; -} - -static void -phy_base_unmap(struct net_device *dev) { - struct macb *bp = netdev_priv(dev); - - if (bp->phy_vbase_addr) - iounmap(bp->phy_vbase_addr); -} - /* DMA buffer descriptor might be different size * depends on hardware configuration: * @@ -2811,13 +2621,6 @@ static int macb_open(struct net_device *dev) if (bp->ptp_info) bp->ptp_info->ptp_init(dev); - bp->phy_reset_times = 0; - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { - bp->poll_task = kthread_create(crc_detect_poll, dev, "crc_poll_%lx", dev->base_addr); - kthread_bind(bp->poll_task, 0); - wake_up_process(bp->poll_task); - } - return 0; } @@ -2828,9 +2631,6 @@ static int macb_close(struct net_device *dev) unsigned long flags; unsigned int q; - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) - kthread_stop(bp->poll_task); - netif_tx_stop_all_queues(dev); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) @@ -2896,7 +2696,6 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) struct gem_stats *hwstat = &bp->hw_stats.gem; struct net_device_stats *nstat = &bp->dev->stats; - mutex_lock(&stats_mutex); gem_update_stats(bp); nstat->rx_errors = (hwstat->rx_frame_check_sequence_errors + @@ -2926,7 +2725,6 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) nstat->tx_aborted_errors = hwstat->tx_excessive_collisions; nstat->tx_carrier_errors = hwstat->tx_carrier_sense_errors; nstat->tx_fifo_errors = hwstat->tx_underrun; - mutex_unlock(&stats_mutex); return nstat; } @@ -2937,11 +2735,9 @@ static void gem_get_ethtool_stats(struct net_device *dev, struct macb *bp; bp = netdev_priv(dev); - mutex_lock(&stats_mutex); gem_update_stats(bp); memcpy(data, &bp->ethtool_stats, sizeof(u64) * (GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES)); - mutex_unlock(&stats_mutex); } static int gem_get_sset_count(struct net_device *dev, int sset) @@ -3675,88 +3471,6 @@ static void macb_configure_caps(struct macb *bp, dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); } -/* only reset phy whose mode is sgmii */ -static void -phy_reset(struct net_device *dev) { - struct macb *bp = netdev_priv(dev); - - if (bp->phy_vbase_addr) { - switch (dev->base_addr) { - case MACB0_ADDR_BASE: - writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); - writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); - break; - case MACB1_ADDR_BASE: - writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); - writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); - break; - case MACB2_ADDR_BASE: - writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); - writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); - break; - case MACB3_ADDR_BASE: - writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); - writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); - break; - default: - break; - } - } -} - -static int crc_detect_poll(void *args) -{ - struct net_device *dev = (struct net_device *)args; - struct macb *bp = netdev_priv(dev); - struct net_device_stats *macb_stats; - unsigned long rx_pkts; - unsigned long rx_pkts_prev; - unsigned long num_crc_err; - unsigned long num_crc_err_prev; - unsigned long percent; - unsigned long rx_pkts_total; - int flag = false; - - phy_base_mmap(dev); - - while (!kthread_should_stop()) { - if (!(bp->link) || (bp->crc_detect_period == 0)) { - ssleep(DEFAULT_SLEEP_S); - continue; - } - - /* 1. detect num of detected rx_pkts - * 2. detect percent of crc error - * 3. phy reset - */ - macb_stats = macb_get_stats(dev); - if (!flag) { - rx_pkts_prev = macb_stats->rx_packets; - num_crc_err_prev = macb_stats->rx_crc_errors; - flag = true; - } - - rx_pkts = macb_stats->rx_packets - rx_pkts_prev; - num_crc_err = macb_stats->rx_crc_errors - num_crc_err_prev; - rx_pkts_total = rx_pkts + num_crc_err; - if ((rx_pkts_total >= bp->nb_rx) && (rx_pkts_total != 0)) { - percent = (num_crc_err * 100) / rx_pkts_total; - if (percent >= bp->crc_err_percent) { - phy_reset(dev); - bp->phy_reset_times++; - } - } - - rx_pkts_prev += rx_pkts; - num_crc_err_prev += num_crc_err; - - msleep(bp->crc_detect_period); - } - - phy_base_unmap(dev); - return 0; -} - static void macb_probe_queues(void __iomem *mem, bool native_io, unsigned int *queue_mask, @@ -4582,9 +4296,6 @@ static int macb_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); - bp->crc_detect_period = DEFAULT_CHECK_FCS_PERIOD_MS; - bp->nb_rx = DEFAULT_CHECK_FCS_RX_THRESHOLD; - bp->crc_err_percent = DEFAULT_CHECK_FCS_PERCENT_THRESHOLD; bp->pdev = pdev; bp->dev = dev; bp->regs = mem; @@ -4613,10 +4324,6 @@ static int macb_probe(struct platform_device *pdev) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); - err = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp); - if (err) - return err; - spin_lock_init(&bp->lock); /* setup capabilities */ @@ -4761,8 +4468,6 @@ static int macb_remove(struct platform_device *pdev) struct macb *bp; struct device_node *np = pdev->dev.of_node; - sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp); - dev = platform_get_drvdata(pdev); if (dev) { -- Gitee From 0b5baf93a72dd341b62450324ff8071c80d5daf5 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 19 Jan 2024 14:50:08 +0800 Subject: [PATCH 29/41] net:macb: Phytium: Support 10G speed This patch add mac function to support 10G speed. Signed-off-by: Li Wencheng Signed-off-by: Li Mingzhe Change-Id: I5eb4bdccb002dd8c96837864fb2eb0be47b3a3b6 --- drivers/net/ethernet/cadence/macb.h | 16 +- drivers/net/ethernet/cadence/macb_main.c | 177 ++++++++++++++++++----- 2 files changed, 153 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index fbdba68ecc..9bbf7b21ba 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -79,11 +79,13 @@ #define MACB_RBQPH 0x04D4 /* GEM register offsets. */ +#define GEM_NCR 0x0000 /* Network Control */ #define GEM_NCFGR 0x0004 /* Network Config */ #define GEM_USRIO 0x000c /* User IO */ #define GEM_DMACFG 0x0010 /* DMA Configuration */ #define GEM_JML 0x0048 /* Jumbo Max Length */ #define GEM_HS_MAC_CONFIG 0x0050 /* GEM high speed config */ +#define GEM_AXI_PIPE 0x0054 /* Axi max pipeline register*/ #define GEM_HRB 0x0080 /* Hash Bottom */ #define GEM_HRT 0x0084 /* Hash Top */ #define GEM_SA1B 0x0088 /* Specific1 Bottom */ @@ -260,6 +262,8 @@ #define MACB_SRTSM_OFFSET 15 #define MACB_OSSMODE_OFFSET 24 /* Enable One Step Synchro Mode */ #define MACB_OSSMODE_SIZE 1 +#define MACB_2PT5G_OFFSET 29 /* 2.5G operation selected */ +#define MACB_2PT5G_SIZE 1 /* Bitfields in NCFGR */ #define MACB_SPD_OFFSET 0 /* Speed */ @@ -301,6 +305,10 @@ #define MACB_IRXFCS_OFFSET 19 #define MACB_IRXFCS_SIZE 1 +/* GEM specific NCR bitfields. */ +#define GEM_ENABLE_HS_MAC_OFFSET 31 /* Use high speed MAC */ +#define GEM_ENABLE_HS_MAC_SIZE 1 + /* GEM specific NCFGR bitfields. */ #define GEM_GBE_OFFSET 10 /* Gigabit mode enable */ #define GEM_GBE_SIZE 1 @@ -538,6 +546,8 @@ #define GEM_RX_SCR_BYPASS_SIZE 1 #define GEM_TX_SCR_BYPASS_OFFSET 8 #define GEM_TX_SCR_BYPASS_SIZE 1 +#define GEM_RX_SYNC_RESET_OFFSET 2 +#define GEM_RX_SYNC_RESET_SIZE 1 #define GEM_TX_EN_OFFSET 1 #define GEM_TX_EN_SIZE 1 #define GEM_SIGNAL_OK_OFFSET 0 @@ -696,11 +706,11 @@ #define MACB_CAPS_GEM_HAS_PTP 0x00000040 #define MACB_CAPS_BD_RD_PREFETCH 0x00000080 #define MACB_CAPS_NEEDS_RSTONUBR 0x00000100 +#define MACB_CAPS_SEL_CLK 0x00000800 #define MACB_CAPS_FIFO_MODE 0x10000000 #define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000 #define MACB_CAPS_SG_DISABLED 0x40000000 #define MACB_CAPS_MACB_IS_GEM 0x80000000 -#define MACB_CAPS_SEL_CLK_HW 0x00001000 /* LSO settings */ #define MACB_LSO_UFO_ENABLE 0x01 @@ -1139,6 +1149,7 @@ struct macb_config { struct clk **rx_clk, struct clk **tsu_clk); int (*init)(struct platform_device *pdev); int jumbo_max_len; + void (*sel_clk_hw)(struct macb *bp); }; struct tsu_incr { @@ -1273,6 +1284,9 @@ struct macb { int tx_bd_rd_prefetch; u32 rx_intr_mask; + + /* phytium sel clk */ + void (*sel_clk_hw)(struct macb *bp); }; #ifdef CONFIG_MACB_USE_HWSTAMP diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index bad8344440..dcb4b0f339 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -82,6 +82,7 @@ #define HS_SPEED_2500M 2 #define HS_SPEED_5000M 3 #define HS_SPEED_10000M 4 +#define MACB_SERDES_RATE_5G 0 #define MACB_SERDES_RATE_10G 1 #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) @@ -414,7 +415,77 @@ static void macb_set_tx_clk(struct clk *clk, int speed, struct net_device *dev) netdev_err(dev, "adjusting tx_clk failed.\n"); } -static int phytium_gem_sel_clk(struct macb *bp) +static int phytium_mac_config(struct macb *bp) +{ + u32 old_ctrl, ctrl; + u32 old_ncr, ncr; + + netdev_dbg(bp->dev, "phytium mac config"); + + old_ncr = macb_readl(bp, NCR); + ncr = old_ncr; + old_ctrl = macb_readl(bp, NCFGR); + ctrl = old_ctrl; + + ncr &= ~(GEM_BIT(ENABLE_HS_MAC) | MACB_BIT(2PT5G)); + ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL) | MACB_BIT(SPD) | MACB_BIT(FD)); + + if (macb_is_gem(bp)) + ctrl &= ~GEM_BIT(GBE); + + if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); + ncr |= MACB_BIT(2PT5G); + } else if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { + ctrl |= GEM_BIT(PCSSEL); + ncr |= GEM_BIT(ENABLE_HS_MAC); + } + + if (bp->duplex) + ctrl |= MACB_BIT(FD); + + /* Apply the new configuration, if any */ + if (old_ctrl ^ ctrl) + macb_or_gem_writel(bp, NCFGR, ctrl); + + if (old_ncr ^ ncr) + macb_or_gem_writel(bp, NCR, ncr); + + return 0; +} + +static void phytium_usx_pcs_link_up(struct macb *bp) +{ + u32 config; + + if (bp->phy_interface != PHY_INTERFACE_MODE_USXGMII) + return; + + netdev_dbg(bp->dev, "phytium usx pcs link up"); + + config = gem_readl(bp, USX_CONTROL); + if (bp->speed == SPEED_10000) { + config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_10G, config); + config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_10000M, config); + } else if (bp->speed == SPEED_5000) { + config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_5G, config); + config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_5000M, config); + } + + /* reset */ + config &= ~(GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN)); + config |= GEM_BIT(RX_SYNC_RESET); + + gem_writel(bp, USX_CONTROL, config); + + /* enable rx and tx */ + config &= ~(GEM_BIT(RX_SYNC_RESET)); + config |= GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN); + + gem_writel(bp, USX_CONTROL, config); +} + +static void phytium_gem1p0_sel_clk(struct macb *bp) { int speed = 0; @@ -525,8 +596,32 @@ static int phytium_gem_sel_clk(struct macb *bp) /*HS_MAC_CONFIG(0x0050) provide rate to the external*/ gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, speed, gem_readl(bp, HS_MAC_CONFIG))); +} - return 0; +static void phytium_gem2p0_sel_clk(struct macb *bp) +{ + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + if (bp->speed == SPEED_100 || bp->speed == SPEED_10) { + gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ + gem_writel(bp, DIV_SEL1_LN, 0x1); /*0x1c0c*/ + } + } + + if (bp->speed == SPEED_100 || bp->speed == SPEED_10) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_100M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_1000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_1000M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_2500) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_2500M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_5000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_5000M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_10000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_10000M, + gem_readl(bp, HS_MAC_CONFIG))); } static void macb_handle_link_change(struct net_device *dev) @@ -584,8 +679,8 @@ static void macb_handle_link_change(struct net_device *dev) macb_set_tx_clk(bp->tx_clk, phydev->speed, dev); /* phytium need hwclock */ - if (bp->caps & MACB_CAPS_SEL_CLK_HW) - phytium_gem_sel_clk(bp); + if ((bp->caps & MACB_CAPS_SEL_CLK) && bp->sel_clk_hw) + bp->sel_clk_hw(bp); netif_carrier_on(dev); netdev_info(dev, "link up (%d/%s)\n", @@ -2366,31 +2461,6 @@ static void macb_configure_dma(struct macb *bp) } } -static int macb_usx_pcs_config(struct macb *bp) -{ - gem_writel(bp, USX_CONTROL, gem_readl(bp, USX_CONTROL) | - GEM_BIT(SIGNAL_OK)); - - return 0; -} - -static void macb_usx_pcs_link_up(struct macb *bp) -{ - u32 config; - - config = gem_readl(bp, USX_CONTROL); - if (bp->speed == SPEED_10000) { - gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_10000M, - gem_readl(bp, HS_MAC_CONFIG))); - config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_10G, config); - config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_10000M, config); - } - - config &= ~(GEM_BIT(TX_SCR_BYPASS) | GEM_BIT(RX_SCR_BYPASS)); - config |= GEM_BIT(TX_EN); - gem_writel(bp, USX_CONTROL, config); -} - static void macb_init_hw(struct macb *bp) { struct macb_queue *queue; @@ -2422,10 +2492,15 @@ static void macb_init_hw(struct macb *bp) if ((bp->caps & MACB_CAPS_JUMBO) && bp->jumbo_max_len) gem_writel(bp, JML, bp->jumbo_max_len); - if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { - macb_usx_pcs_config(bp); + gem_writel(bp, AXI_PIPE, 0x1010); + if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || + bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + /* phytium need hwclock */ + if (bp->caps & MACB_CAPS_SEL_CLK) + bp->sel_clk_hw(bp); + phytium_mac_config(bp); if (bp->link) - macb_usx_pcs_link_up(bp); + phytium_usx_pcs_link_up(bp); } else { bp->speed = SPEED_10; bp->duplex = DUPLEX_HALF; @@ -4152,16 +4227,30 @@ static const struct macb_config zynq_config = { .init = macb_init, }; -static const struct macb_config phytium_config = { +static const struct macb_config phytium_gem1p0_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK, + .dma_burst_length = 16, + .clk_init = macb_clk_init, + .init = macb_init, + .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem1p0_sel_clk, +}; + +static const struct macb_config phytium_gem2p0_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_SEL_CLK_HW, + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem2p0_sel_clk, }; static const struct of_device_id macb_dt_ids[] = { @@ -4179,7 +4268,8 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,emac", .data = &emac_config }, { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config}, { .compatible = "cdns,zynq-gem", .data = &zynq_config }, - { .compatible = "cdns,phytium-gem", .data = &phytium_config }, + { .compatible = "cdns,phytium-gem-1.0", .data = &phytium_gem1p0_config }, + { .compatible = "cdns,phytium-gem-2.0", .data = &phytium_gem2p0_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, macb_dt_ids); @@ -4187,7 +4277,7 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id macb_acpi_ids[] = { - { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_config }, + { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_gem1p0_config }, { } }; @@ -4319,6 +4409,9 @@ static int macb_probe(struct platform_device *pdev) if (macb_config) bp->jumbo_max_len = macb_config->jumbo_max_len; + if (macb_config) + bp->sel_clk_hw = macb_config->sel_clk_hw; + bp->wol = 0; if (device_property_read_bool(&pdev->dev, "magic-packet")) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; @@ -4395,6 +4488,12 @@ static int macb_probe(struct platform_device *pdev) bp->speed = SPEED_10000; } + if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + bp->link = 1; + bp->duplex = 1; + bp->speed = SPEED_2500; + } + /* IP specific init */ err = init(pdev); if (err) -- Gitee From 320f7c23b8cf70e9b1915fdac27b23b091aa72a5 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 2 Feb 2024 10:44:28 +0800 Subject: [PATCH 30/41] net:macb: Support 10G mac ethtool settings Add macb 10g ethtool get_link_ksettings and set_link_ksettings function. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7c6ccf3c6b29efc914fe9513e2bbf361a53e193c --- drivers/net/ethernet/cadence/macb_main.c | 70 +++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index dcb4b0f339..6f532c8ae2 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -3399,6 +3399,72 @@ static int gem_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) return ret; } +static int macb_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *kset) +{ + int ret = 0; + struct macb *bp = netdev_priv(ndev); + u32 supported = 0; + u32 advertising = 0; + + if (!ndev->phydev) { + if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || + bp->phy_interface == PHY_INTERFACE_MODE_XGMII) { + supported = SUPPORTED_10000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_10000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + supported = SUPPORTED_2500baseX_Full | SUPPORTED_1000baseT_Full + | SUPPORTED_100baseT_Full | SUPPORTED_10baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full | ADVERTISED_1000baseT_Full + | ADVERTISED_100baseT_Full | ADVERTISED_10baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_TP; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_TP; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII) { + supported = SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_TP; + advertising = ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_TP; + } + + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.advertising, + advertising); + kset->base.speed = bp->speed; + kset->base.duplex = bp->duplex; + } else { + phy_ethtool_get_link_ksettings(ndev, kset); + } + + return ret; +} + +static int macb_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *kset) +{ + int ret = 0; + + if (!ndev->phydev) { + netdev_err(ndev, "fixed link interface not supported set link\n"); + ret = -EOPNOTSUPP; + } else { + phy_ethtool_set_link_ksettings(ndev, kset); + } + + return ret; +} + static const struct ethtool_ops macb_ethtool_ops = { .get_regs_len = macb_get_regs_len, .get_regs = macb_get_regs, @@ -3420,8 +3486,8 @@ static const struct ethtool_ops gem_ethtool_ops = { .get_ethtool_stats = gem_get_ethtool_stats, .get_strings = gem_get_ethtool_strings, .get_sset_count = gem_get_sset_count, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link_ksettings = macb_get_link_ksettings, + .set_link_ksettings = macb_set_link_ksettings, .get_ringparam = macb_get_ringparam, .set_ringparam = macb_set_ringparam, .get_rxnfc = gem_get_rxnfc, -- Gitee From 0c6ef5dc4c479139dfeb50bf77061d01b82e6a1d Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 23 Feb 2024 17:31:18 +0800 Subject: [PATCH 31/41] net/macb: Bugfix a DMA conflict problem The NIC does not work after receiving 511 packets because the CIRS_SPACE calculation result is 511, which causing the rx dma ring not to be fully initialized and resulting in a DMA conflict. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Icefe5bd833f0f9914779ed87cca1bbfff21d2951 --- drivers/net/ethernet/cadence/macb_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 6f532c8ae2..bb784eb3c3 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2289,6 +2289,15 @@ static void gem_init_rings(struct macb *bp) queue->tx_head = 0; queue->tx_tail = 0; + for (i = 0; i < bp->rx_ring_size; i++) { + desc = macb_rx_desc(queue, i); + desc->ctrl = 0; + /* make sure ctrl is cleared first, + * and bit RX_USED is set to avoid a race. + */ + dma_wmb(); + desc->addr |= MACB_BIT(RX_USED); + } queue->rx_tail = 0; queue->rx_prepared_head = 0; -- Gitee From a4a9cc2c8e34de4afbd165fadfb991c60a12f125 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 23 Feb 2024 17:36:08 +0800 Subject: [PATCH 32/41] net/macb: Bugfix Macb queue high address inconsistent problem The NIC did not work due to inconsistent Macb queue high addresses. Multiple queues share a high address register, and it is necessary to ensure that the high address allocation is consistent. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7000beaf7411beaff06c33efb1a4396925b1cea5 --- drivers/net/ethernet/cadence/macb_main.c | 129 +++++++++++++++++++---- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index bb784eb3c3..3f546f24cb 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -40,6 +40,9 @@ #include #include "macb.h" +#define MAX_RING_ADDR_ALLOC_TIMES 3 +#define RING_ADDR_INTERVAL 128 + #define MACB_RX_BUFFER_SIZE 128 #define RX_BUFFER_MULTIPLE 64 /* bytes */ @@ -2170,27 +2173,48 @@ static void macb_free_rx_buffers(struct macb *bp) static void macb_free_consistent(struct macb *bp) { + struct macb_dma_desc *tx_ring_base = NULL; + struct macb_dma_desc *rx_ring_base = NULL; + dma_addr_t tx_ring_base_addr; + dma_addr_t rx_ring_base_addr; struct macb_queue *queue; unsigned int q; int size; bp->macbgem_ops.mog_free_rx_buffers(bp); + queue = bp->queues; + if (queue->tx_ring) { + tx_ring_base = queue->tx_ring; + tx_ring_base_addr = queue->tx_ring_dma; + } + if (queue->rx_ring) { + rx_ring_base = queue->rx_ring; + rx_ring_base_addr = queue->rx_ring_dma; + } + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { kfree(queue->tx_skb); queue->tx_skb = NULL; - if (queue->tx_ring) { - size = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch; - dma_free_coherent(&bp->pdev->dev, size, - queue->tx_ring, queue->tx_ring_dma); + if (queue->tx_ring) queue->tx_ring = NULL; - } - if (queue->rx_ring) { - size = RX_RING_BYTES(bp) + bp->rx_bd_rd_prefetch; - dma_free_coherent(&bp->pdev->dev, size, - queue->rx_ring, queue->rx_ring_dma); + if (queue->rx_ring) queue->rx_ring = NULL; - } + } + + if (tx_ring_base) { + size = bp->num_queues * (TX_RING_BYTES(bp) + + bp->tx_bd_rd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(&bp->pdev->dev, size, tx_ring_base, + tx_ring_base_addr); + } + if (rx_ring_base) { + size = bp->num_queues * (RX_RING_BYTES(bp) + + bp->rx_bd_rd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(&bp->pdev->dev, size, rx_ring_base, + rx_ring_base_addr); } } @@ -2230,17 +2254,87 @@ static int macb_alloc_rx_buffers(struct macb *bp) return 0; } +static int macb_queue_phyaddr_check(struct macb *bp, dma_addr_t ring_base_addr, + int offset) +{ + u32 bus_addr_high; + int i; + + bus_addr_high = upper_32_bits(ring_base_addr); + for (i = 1; i < bp->num_queues; i++) { + ring_base_addr += offset; + if (bus_addr_high != upper_32_bits(ring_base_addr)) + return -1; + } + + return 0; +} + static int macb_alloc_consistent(struct macb *bp) { + struct macb_dma_desc *tx_ring_base, *rx_ring_base; + dma_addr_t tx_ring_base_addr, rx_ring_base_addr; struct macb_queue *queue; + int tx_offset, rx_offset; + int tx_size, rx_size; unsigned int q; + int ret, i; int size; + tx_offset = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch + + RING_ADDR_INTERVAL; + tx_size = bp->num_queues * tx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + return -ENOMEM; + + tx_ring_base = dma_alloc_coherent(&bp->pdev->dev, tx_size, + &tx_ring_base_addr, + GFP_KERNEL); + if (!tx_ring_base) + continue; + + ret = macb_queue_phyaddr_check(bp, tx_ring_base_addr, + tx_offset); + if (ret) { + dma_free_coherent(&bp->pdev->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + continue; + } else { + break; + } + } + + rx_offset = RX_RING_BYTES(bp) + bp->rx_bd_rd_prefetch + + RING_ADDR_INTERVAL; + rx_size = bp->num_queues * rx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) { + dma_free_coherent(&bp->pdev->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + return -ENOMEM; + } + + rx_ring_base = dma_alloc_coherent(&bp->pdev->dev, rx_size, + &rx_ring_base_addr, + GFP_KERNEL); + if (!rx_ring_base) + continue; + + ret = macb_queue_phyaddr_check(bp, rx_ring_base_addr, + rx_offset); + if (ret) { + dma_free_coherent(&bp->pdev->dev, rx_size, rx_ring_base, + rx_ring_base_addr); + continue; + } else { + break; + } + } + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - size = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch; - queue->tx_ring = dma_alloc_coherent(&bp->pdev->dev, size, - &queue->tx_ring_dma, - GFP_KERNEL); + queue->tx_ring = (void *)tx_ring_base + q * tx_offset; + queue->tx_ring_dma = tx_ring_base_addr + q * tx_offset; if (!queue->tx_ring) goto out_err; netdev_dbg(bp->dev, @@ -2249,13 +2343,12 @@ static int macb_alloc_consistent(struct macb *bp) queue->tx_ring); size = bp->tx_ring_size * sizeof(struct macb_tx_skb); - queue->tx_skb = kmalloc(size, GFP_KERNEL); + queue->tx_skb = kzalloc(size, GFP_KERNEL); if (!queue->tx_skb) goto out_err; - size = RX_RING_BYTES(bp) + bp->rx_bd_rd_prefetch; - queue->rx_ring = dma_alloc_coherent(&bp->pdev->dev, size, - &queue->rx_ring_dma, GFP_KERNEL); + queue->rx_ring = (void *)rx_ring_base + q * rx_offset; + queue->rx_ring_dma = rx_ring_base_addr + q * rx_offset; if (!queue->rx_ring) goto out_err; netdev_dbg(bp->dev, -- Gitee From 2860bcfd964a1f1dd8c1d63cb0bcf4f99c865b8b Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 19 Jan 2024 14:46:31 +0800 Subject: [PATCH 33/41] dt-bindings: Phytium: Add bindings for Phytium MAC driver to support 10G speed This patch is a Documentation for Phytium MAC driver to support 10G speed. Signed-off-by: Li Wencheng Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Change-Id: I62116aa16a0e0b9972c9bde5bc322467d7a2ce99 --- Documentation/devicetree/bindings/net/macb.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index 27168b1169..9f6ecd9844 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -15,7 +15,8 @@ Required properties: Use "atmel,sama5d4-gem" for the GEM IP (10/100) available on Atmel sama5d4 SoCs. Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC. Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC. - Use "cdns,phytium-gem" for Phytium SoCs. + Use "cdns,phytium-gem-1.0" for Phytium SoCs. + Use "cdns,phytium-gem-2.0" for Phytium SoCs. Or the generic form: "cdns,emac". - reg: Address and length of the register set for the device - interrupts: Should contain macb interrupt -- Gitee From d19aea6169c0fc9248461678598271edd9349142 Mon Sep 17 00:00:00 2001 From: Jiakun Shuai Date: Thu, 31 Oct 2024 15:30:40 +0800 Subject: [PATCH 34/41] arm64: dts: phytium: Fix macb compatible for E2000 Update "phytium-gem" to "phytium-gem-1.0" for E2000 series. Signed-off-by: Jiakun Shuai --- arch/arm64/boot/dts/phytium/pe2201.dtsi | 6 +++--- arch/arm64/boot/dts/phytium/pe2202.dtsi | 8 ++++---- arch/arm64/boot/dts/phytium/pe2204.dtsi | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index ea26cbd986..25885f6946 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -384,7 +384,7 @@ sgpio: sgpio@2807d000 { }; macb0: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -397,7 +397,7 @@ macb0: ethernet@3200e000 { }; macb1: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -410,7 +410,7 @@ macb1: ethernet@32010000 { }; macb2: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe2202.dtsi b/arch/arm64/boot/dts/phytium/pe2202.dtsi index 3493153b39..786a7f0a2e 100644 --- a/arch/arm64/boot/dts/phytium/pe2202.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2202.dtsi @@ -125,7 +125,7 @@ sata1: sata@32014000 { }; macb0: ethernet@3200c000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200c000 0x0 0x2000>; interrupts = , , @@ -142,7 +142,7 @@ macb0: ethernet@3200c000 { }; macb1: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -155,7 +155,7 @@ macb1: ethernet@3200e000 { }; macb2: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -169,7 +169,7 @@ macb2: ethernet@32010000 { }; macb3: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe2204.dtsi b/arch/arm64/boot/dts/phytium/pe2204.dtsi index fa1e9a8488..153ad7ba5f 100644 --- a/arch/arm64/boot/dts/phytium/pe2204.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2204.dtsi @@ -153,7 +153,7 @@ sata1: sata@32014000 { }; macb0: ethernet@3200c000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200c000 0x0 0x2000>; interrupts = , , @@ -171,7 +171,7 @@ macb0: ethernet@3200c000 { }; macb1: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -184,7 +184,7 @@ macb1: ethernet@3200e000 { }; macb2: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -197,7 +197,7 @@ macb2: ethernet@32010000 { }; macb3: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , -- Gitee From 0fe728467710e5dbf20c3c69affcc99ce9c68e89 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 5 Sep 2024 11:59:08 +0800 Subject: [PATCH 35/41] net/macb: Supported S4 suspend and resume function The MPE bit needs to be enabled after resume and this patch rewrites macb suspend and resume functions. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I935f8a1b8f43fcf781770fd9e100e2cf236183d4 --- drivers/net/ethernet/cadence/macb.h | 6 ++ drivers/net/ethernet/cadence/macb_main.c | 76 +++++++++++++++++++++--- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 9bbf7b21ba..5818ca5b69 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1141,6 +1141,11 @@ struct macb_ptp_info { struct ifreq *ifr, int cmd); }; +struct macb_pm_data { + u32 scrt2; + u32 usrio; +}; + struct macb_config { u32 caps; unsigned int dma_burst_length; @@ -1284,6 +1289,7 @@ struct macb { int tx_bd_rd_prefetch; u32 rx_intr_mask; + struct macb_pm_data pm_data; /* phytium sel clk */ void (*sel_clk_hw)(struct macb *bp); diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 3f546f24cb..ab1ec1800d 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1170,7 +1170,6 @@ static void gem_rx_refill(struct macb_queue *queue) break; } - queue->rx_prepared_head++; queue->rx_skbuff[entry] = skb; if (entry == bp->rx_ring_size - 1) @@ -1189,6 +1188,7 @@ static void gem_rx_refill(struct macb_queue *queue) dma_wmb(); desc->addr &= ~MACB_BIT(RX_USED); } + queue->rx_prepared_head++; } /* Make descriptor updates visible to hardware */ @@ -2279,7 +2279,7 @@ static int macb_alloc_consistent(struct macb *bp) int tx_size, rx_size; unsigned int q; int ret, i; - int size; + int size = 0; tx_offset = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch + RING_ADDR_INTERVAL; @@ -2573,6 +2573,10 @@ static void macb_init_hw(struct macb *bp) macb_reset_hw(bp); macb_set_hwaddr(bp); + config = macb_readl(bp, NCR); + config |= MACB_BIT(MPE); + macb_writel(bp, NCR, config); + config = macb_mdc_clk_div(bp); if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) config |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); @@ -4770,6 +4774,12 @@ static int __maybe_unused macb_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct net_device *netdev = platform_get_drvdata(pdev); struct macb *bp = netdev_priv(netdev); + struct macb_queue *queue = bp->queues; + unsigned long flags; + unsigned int q; + + if (!netif_running(netdev)) + return 0; netif_carrier_off(netdev); netif_device_detach(netdev); @@ -4779,12 +4789,35 @@ static int __maybe_unused macb_suspend(struct device *dev) macb_writel(bp, WOL, MACB_BIT(MAG)); enable_irq_wake(bp->queues[0].irq); } else { + for (q = 0, queue = bp->queues; q < bp->num_queues; + ++q, ++queue) + napi_disable(&queue->napi); + if (netdev->phydev) { + phy_stop(netdev->phydev); + phy_suspend(netdev->phydev); + } + + spin_lock_irqsave(&bp->lock, flags); + macb_reset_hw(bp); + spin_unlock_irqrestore(&bp->lock, flags); + } + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + bp->pm_data.usrio = macb_or_gem_readl(bp, USRIO); + + if (netdev->hw_features & NETIF_F_NTUPLE) + bp->pm_data.scrt2 = gem_readl_n(bp, ETHT, SCRT2_ETHT); + + if (bp->ptp_info) + bp->ptp_info->ptp_remove(netdev); + + if (!(device_may_wakeup(&bp->dev->dev))) { clk_disable_unprepare(bp->tx_clk); clk_disable_unprepare(bp->hclk); clk_disable_unprepare(bp->pclk); clk_disable_unprepare(bp->rx_clk); - clk_disable_unprepare(bp->tsu_clk); } + clk_disable_unprepare(bp->tsu_clk); return 0; } @@ -4794,12 +4827,13 @@ static int __maybe_unused macb_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct net_device *netdev = platform_get_drvdata(pdev); struct macb *bp = netdev_priv(netdev); + struct macb_queue *queue = bp->queues; + unsigned int q; - if (bp->wol & MACB_WOL_ENABLED) { - macb_writel(bp, IDR, MACB_BIT(WOL)); - macb_writel(bp, WOL, 0); - disable_irq_wake(bp->queues[0].irq); - } else { + if (!netif_running(netdev)) + return 0; + + if (!(device_may_wakeup(&bp->dev->dev))) { clk_prepare_enable(bp->pclk); clk_prepare_enable(bp->hclk); clk_prepare_enable(bp->tx_clk); @@ -4807,7 +4841,33 @@ static int __maybe_unused macb_resume(struct device *dev) } clk_prepare_enable(bp->tsu_clk); + if (bp->wol & MACB_WOL_ENABLED) { + macb_writel(bp, IDR, MACB_BIT(WOL)); + macb_writel(bp, WOL, 0); + disable_irq_wake(bp->queues[0].irq); + } else { + for (q = 0, queue = bp->queues; q < bp->num_queues; + ++q, ++queue) + napi_enable(&queue->napi); + if (netdev->phydev) { + phy_resume(netdev->phydev); + phy_start(netdev->phydev); + } + } + + if (netdev->hw_features & NETIF_F_NTUPLE) + gem_writel_n(bp, ETHT, SCRT2_ETHT, bp->pm_data.scrt2); + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + macb_or_gem_writel(bp, USRIO, bp->pm_data.usrio); + + bp->macbgem_ops.mog_init_rings(bp); + macb_init_hw(bp); + macb_set_rx_mode(netdev); netif_device_attach(netdev); + netif_carrier_on(netdev); + if (bp->ptp_info) + bp->ptp_info->ptp_init(netdev); return 0; } -- Gitee From 1fa8a72b96ae939fe223f0fcfb8eda64b2392f8d Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 26 Sep 2024 15:50:06 +0800 Subject: [PATCH 36/41] net/macb: Add support for 2.5G speed Adapt to 2.5G, including 2.5G electrical ports and 2.5G optical ports. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I2b1d7d4be521535c101bfcb1057f85e9526d6b2c --- drivers/net/ethernet/cadence/macb.h | 5 +++ drivers/net/ethernet/cadence/macb_main.c | 50 ++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 5818ca5b69..23287be81e 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -161,6 +161,7 @@ #define GEM_PEFTN 0x01f4 /* PTP Peer Event Frame Tx Ns */ #define GEM_PEFRSL 0x01f8 /* PTP Peer Event Frame Rx Sec Low */ #define GEM_PEFRN 0x01fc /* PTP Peer Event Frame Rx Ns */ +#define GEM_PCSCTRL 0x0200 /* PCS control register*/ #define GEM_DCFG1 0x0280 /* Design Config 1 */ #define GEM_DCFG2 0x0284 /* Design Config 2 */ #define GEM_DCFG3 0x0288 /* Design Config 3 */ @@ -498,6 +499,10 @@ #define GEM_HS_MAC_SPEED_OFFSET 0 #define GEM_HS_MAC_SPEED_SIZE 3 +/* Bitfields in PCSCNTRL */ +#define GEM_PCSAUTONEG_OFFSET 12 +#define GEM_PCSAUTONEG_SIZE 1 + /* Bitfields in DCFG1. */ #define GEM_IRQCOR_OFFSET 23 #define GEM_IRQCOR_SIZE 1 diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index ab1ec1800d..1764d15434 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -422,6 +422,7 @@ static int phytium_mac_config(struct macb *bp) { u32 old_ctrl, ctrl; u32 old_ncr, ncr; + u32 pcsctrl; netdev_dbg(bp->dev, "phytium mac config"); @@ -437,7 +438,7 @@ static int phytium_mac_config(struct macb *bp) ctrl &= ~GEM_BIT(GBE); if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); + ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL) | GEM_BIT(GBE); ncr |= MACB_BIT(2PT5G); } else if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { ctrl |= GEM_BIT(PCSSEL); @@ -454,6 +455,12 @@ static int phytium_mac_config(struct macb *bp) if (old_ncr ^ ncr) macb_or_gem_writel(bp, NCR, ncr); + if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + pcsctrl = gem_readl(bp, PCSCTRL); + pcsctrl &= ~GEM_BIT(PCSAUTONEG); + gem_writel(bp, PCSCTRL, pcsctrl); + } + return 0; } @@ -500,6 +507,24 @@ static void phytium_gem1p0_sel_clk(struct macb *bp) gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ speed = HS_SPEED_10000M; } + } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + if (bp->speed == SPEED_2500) { + gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ + gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ + gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ + gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + gem_writel(bp, TX_CLK_SEL0, 0x0); /*0x1c20*/ + gem_writel(bp, TX_CLK_SEL1, 0x1); /*0x1c24*/ + gem_writel(bp, TX_CLK_SEL2, 0x1); /*0x1c28*/ + gem_writel(bp, TX_CLK_SEL3, 0x1); /*0x1c2c*/ + gem_writel(bp, RX_CLK_SEL0, 0x1); /*0x1c30*/ + gem_writel(bp, RX_CLK_SEL1, 0x0); /*0x1c34*/ + gem_writel(bp, TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + gem_writel(bp, TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + speed = HS_SPEED_2500M; + } } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { if (bp->speed == SPEED_2500) { gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ @@ -633,6 +658,7 @@ static void macb_handle_link_change(struct net_device *dev) struct phy_device *phydev = dev->phydev; unsigned long flags; int err, status_change = 0; + u32 network_ctrl, pcsctrl, old_pcsctrl; spin_lock_irqsave(&bp->lock, flags); @@ -650,7 +676,7 @@ static void macb_handle_link_change(struct net_device *dev) reg |= MACB_BIT(FD); if (phydev->speed == SPEED_100) reg |= MACB_BIT(SPD); - if (phydev->speed == SPEED_1000 && + if ((phydev->speed == SPEED_1000 || phydev->speed == SPEED_2500) && bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE) reg |= GEM_BIT(GBE); @@ -659,6 +685,17 @@ static void macb_handle_link_change(struct net_device *dev) bp->speed = phydev->speed; bp->duplex = phydev->duplex; status_change = 1; + + if (bp->speed == SPEED_2500) { + network_ctrl = macb_readl(bp, NCR); + network_ctrl |= MACB_BIT(2PT5G); + macb_writel(bp, NCR, network_ctrl); + + old_pcsctrl = gem_readl(bp, PCSCTRL); + pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG); + if (old_pcsctrl != pcsctrl) + gem_writel(bp, PCSCTRL, pcsctrl); + } } } @@ -3522,7 +3559,14 @@ static int macb_get_link_ksettings(struct net_device *ndev, | ADVERTISED_FIBRE | ADVERTISED_Pause; kset->base.port = PORT_FIBRE; kset->base.transceiver = XCVR_INTERNAL; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + supported = SUPPORTED_2500baseX_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { supported = SUPPORTED_2500baseX_Full | SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; -- Gitee From 91aae0676369bb99f4375f30a023b594fe5b788a Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 5 Nov 2024 10:34:43 +0800 Subject: [PATCH 37/41] net/macb:Change the minimum rx ring size to 128 If the budget is equal to 64, gem_rx can exit after receiving a maximum of budget packets. If the ring size is changed to 64, a descriptor that has not been refilled is considered to be a valid descriptor to be received, and the error "inconsistent Rx descriptor chain" is reported. So we need to limit the minimum rx ring size to be greater than 64. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ib86ec03d83f2f7324022cb652d3a5879078b429e --- drivers/net/ethernet/cadence/macb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 1764d15434..1dbe3707db 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -47,7 +47,7 @@ #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ -#define MIN_RX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 128 #define MAX_RX_RING_SIZE 8192 #define RX_RING_BYTES(bp) (macb_dma_desc_get_size(bp) \ * (bp)->rx_ring_size) -- Gitee From 961a36b942a12834965776f58955db698a1e4a9f Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 5 Nov 2024 10:35:22 +0800 Subject: [PATCH 38/41] net/macb:Add polling task for phy reset function and mac restart Add polling tasks:PHY reset when CRC error packets are detected or suspend/resume when MAC and PHY states are inconsistent; Signed-off-by: Li Wencheng Change-Id: I93fd869b86660b5e88faafb9d4c715fad5db6b8a --- drivers/net/ethernet/cadence/macb.h | 10 + drivers/net/ethernet/cadence/macb_main.c | 422 +++++++++++++++++++++++ 2 files changed, 432 insertions(+) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 23287be81e..6e24eaaca6 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -14,6 +14,7 @@ #include #include #include +#include #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) #define MACB_EXT_DESC @@ -502,6 +503,8 @@ /* Bitfields in PCSCNTRL */ #define GEM_PCSAUTONEG_OFFSET 12 #define GEM_PCSAUTONEG_SIZE 1 +#define GEM_PCS_RESET_OFFSET 15 +#define GEM_PCS_RESET_SIZE 1 /* Bitfields in DCFG1. */ #define GEM_IRQCOR_OFFSET 23 @@ -1298,6 +1301,13 @@ struct macb { /* phytium sel clk */ void (*sel_clk_hw)(struct macb *bp); + + void *phy_vbase_addr; + unsigned long crc_detect_period; /* default period: 0 ms; disable crc detect function */ + unsigned long nb_rx; /* CRC detection starts when more than nb_rx packets are received */ + unsigned long crc_err_percent; /* PHY RESET when crc error more than crc_err_percent */ + unsigned long phy_reset_times; + struct task_struct *poll_task; }; #ifdef CONFIG_MACB_USE_HWSTAMP diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 1dbe3707db..ac78992626 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include "macb.h" #define MAX_RING_ADDR_ALLOC_TIMES 3 @@ -96,6 +99,193 @@ */ #define MACB_HALT_TIMEOUT 1230 +#define DEFAULT_SLEEP_S 1 +#define DEFAULT_CHECK_FCS_PERIOD_MS 2000 +#define DEFAULT_CHECK_FCS_RX_THRESHOLD 2 +#define DEFAULT_CHECK_FCS_PERCENT_THRESHOLD 1 + +#define PHY0_ADDR_BASE 0x32140000 +#define PHY0_ADDR_OFFSET 0x254 +#define PHY1_ADDR_BASE 0x32240000 +#define PHY1_ADDR_OFFSET 0x08c +#define PHY2_ADDR_BASE 0x32340000 +#define PHY2_ADDR_OFFSET 0x254 +#define PHY3_ADDR_BASE 0x32440000 +#define PHY3_ADDR_OFFSET 0x254 + +#define MACB0_ADDR_BASE 0x3200c000 +#define MACB1_ADDR_BASE 0x3200e000 +#define MACB2_ADDR_BASE 0x32010000 +#define MACB3_ADDR_BASE 0x32012000 + +#define MAP_SIZE 4096 +#define MACB_POLL_VERSION "1.0" + +static DEFINE_MUTEX(stats_mutex); +static int crc_detect_poll(void *args); +static int __maybe_unused macb_suspend(struct device *dev); +static int __maybe_unused macb_resume(struct device *dev); + +static ssize_t +check_fcs_period_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->crc_detect_period); +} + +static ssize_t +check_fcs_period_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long crc_detect_period; + + if (kstrtoul(buf, 0, &crc_detect_period) != 0) + return -EINVAL; + + if (crc_detect_period < 0) + return -EINVAL; + bp->crc_detect_period = crc_detect_period; + + return err ? err : count; +} +static DEVICE_ATTR_RW(check_fcs_period); + +static ssize_t +check_fcs_rx_threshold_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->nb_rx); +} + +static ssize_t +check_fcs_rx_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long nb_rx; + + if (kstrtoul(buf, 0, &nb_rx) != 0) + return -EINVAL; + + if (nb_rx < 0) + return -EINVAL; + bp->nb_rx = nb_rx; + + return err ? err : count; +} +static DEVICE_ATTR_RW(check_fcs_rx_threshold); + +static ssize_t +check_fcs_percent_threshold_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->crc_err_percent); +} + +static ssize_t +check_fcs_percent_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long crc_err_percent; + + if (kstrtoul(buf, 0, &crc_err_percent) != 0) + return -EINVAL; + + if (crc_err_percent > 100 || crc_err_percent < 0) + return -EINVAL; + bp->crc_err_percent = crc_err_percent; + + return err ? err : count; +} +static DEVICE_ATTR_RW(check_fcs_percent_threshold); + +static ssize_t +phy_reset_times_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->phy_reset_times); +} +static DEVICE_ATTR_RO(phy_reset_times); + +static struct attribute *dev_attrs[] = { + &dev_attr_check_fcs_period.attr, + &dev_attr_check_fcs_rx_threshold.attr, + &dev_attr_check_fcs_percent_threshold.attr, + &dev_attr_phy_reset_times.attr, + NULL, +}; + +static const struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +static void *get_mmap_addr(unsigned long phy_addr) +{ + void *virt_addr; + + virt_addr = ioremap(phy_addr, MAP_SIZE); + + if (!virt_addr) { + pr_err("Failed to map physical address to virtual address\n"); + return NULL; + } + + return virt_addr; +} + +static void phy_base_mmap(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + void *base = NULL; + + switch (dev->base_addr) { + case MACB0_ADDR_BASE: + base = get_mmap_addr(PHY0_ADDR_BASE); + break; + case MACB1_ADDR_BASE: + base = get_mmap_addr(PHY1_ADDR_BASE); + break; + case MACB2_ADDR_BASE: + base = get_mmap_addr(PHY2_ADDR_BASE); + break; + case MACB3_ADDR_BASE: + base = get_mmap_addr(PHY3_ADDR_BASE); + break; + default: + break; + } + + bp->phy_vbase_addr = base; +} + +static void phy_base_unmap(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + + if (bp->phy_vbase_addr) + iounmap(bp->phy_vbase_addr); +} + /* DMA buffer descriptor might be different size * depends on hardware configuration: * @@ -2839,6 +3029,14 @@ static int macb_open(struct net_device *dev) if (bp->ptp_info) bp->ptp_info->ptp_init(dev); + bp->phy_reset_times = 0; + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + bp->poll_task = kthread_create(crc_detect_poll, dev, + "crc_poll_%lx", dev->base_addr); + kthread_bind(bp->poll_task, 0); + wake_up_process(bp->poll_task); + } + return 0; } @@ -2849,6 +3047,9 @@ static int macb_close(struct net_device *dev) unsigned long flags; unsigned int q; + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) + kthread_stop(bp->poll_task); + netif_tx_stop_all_queues(dev); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) @@ -2914,6 +3115,7 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) struct gem_stats *hwstat = &bp->hw_stats.gem; struct net_device_stats *nstat = &bp->dev->stats; + mutex_lock(&stats_mutex); gem_update_stats(bp); nstat->rx_errors = (hwstat->rx_frame_check_sequence_errors + @@ -2943,6 +3145,7 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) nstat->tx_aborted_errors = hwstat->tx_excessive_collisions; nstat->tx_carrier_errors = hwstat->tx_carrier_sense_errors; nstat->tx_fifo_errors = hwstat->tx_underrun; + mutex_unlock(&stats_mutex); return nstat; } @@ -2953,9 +3156,11 @@ static void gem_get_ethtool_stats(struct net_device *dev, struct macb *bp; bp = netdev_priv(dev); + mutex_lock(&stats_mutex); gem_update_stats(bp); memcpy(data, &bp->ethtool_stats, sizeof(u64) * (GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES)); + mutex_unlock(&stats_mutex); } static int gem_get_sset_count(struct net_device *dev, int sset) @@ -3762,6 +3967,214 @@ static void macb_configure_caps(struct macb *bp, dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); } +/* only reset phy whose mode is sgmii */ +static void phy_reset(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + + if (bp->phy_vbase_addr) { + switch (dev->base_addr) { + case MACB0_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); + break; + case MACB1_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); + break; + case MACB2_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); + break; + case MACB3_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); + break; + default: + break; + } + } +} + +static int macb_pcs_software_reset(struct macb *bp, bool reset) +{ + u32 value = gem_readl(bp, PCSCTRL); + + if (reset) + value |= GEM_BIT(PCS_RESET); + else + value &= ~GEM_BIT(PCS_RESET); + + gem_writel(bp, PCSCTRL, value); + msleep(20); + + return 0; +} + +static int macb_check_link_speed_duplex(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; + struct device *device = &bp->pdev->dev; + unsigned int reg_data = 0; + unsigned int pcs_select_mode = 0; + unsigned int duplex = 0; + unsigned int speed = 0; + unsigned int need_serdes_reset = 0; + static unsigned int link_error_count; + + reg_data = gem_readl(bp, NCFGR); + pcs_select_mode = (reg_data >> MACB_CLK_OFFSET) & 3; + duplex = (reg_data >> MACB_FD_OFFSET) & 1; + speed = (reg_data >> MACB_SPD_OFFSET) & 1; + + if (phydev->duplex == DUPLEX_FULL && phydev->speed == SPEED_1000) { + if (pcs_select_mode != 3 || duplex != 1 || speed != 0) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_FULL && phydev->speed == SPEED_100) { + if (pcs_select_mode != 2 || duplex != 1 || speed != 1) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_FULL && phydev->speed == SPEED_10) { + if (pcs_select_mode != 2 || duplex != 1 || speed != 0) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_HALF && phydev->speed == SPEED_100) { + if (pcs_select_mode != 2 || duplex != 0 || speed != 1) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_HALF && phydev->speed == SPEED_10) { + if (pcs_select_mode != 2 || duplex != 0 || speed != 0) + need_serdes_reset = 1; + } + + if (!need_serdes_reset) { + link_error_count = 0; + return 0; + } + + if (link_error_count == 3) { + netdev_crit(dev, "%s failed, so reset sgmii serdes and reset device\n", + __func__); + phy_reset(dev); + bp->phy_reset_times++; + + if (!rtnl_trylock()) { + netdev_crit(dev, "%s rtnl_trylock failed\n", __func__); + return -EINVAL; + } + macb_suspend(device); + macb_resume(device); + rtnl_unlock(); + link_error_count = 0; + } else { + link_error_count++; + } + + return -EINVAL; +} + +static int crc_detect_poll(void *args) +{ + struct net_device *dev = (struct net_device *)args; + struct macb *bp = netdev_priv(dev); + struct net_device_stats *macb_stats; + struct device *device = &bp->pdev->dev; + unsigned long rx_pkts; + unsigned long rx_pkts_prev = 0; + unsigned long num_crc_err; + unsigned long num_crc_err_prev = 0; + unsigned long percent; + unsigned long rx_pkts_total; + unsigned long num_err_drop; + unsigned long num_err_drop_total = 0; + int flag = false; + int reset_flag; + int pcs_link; + int rx_error_count = 0; + + phy_base_mmap(dev); + netdev_info(bp->dev, "%s version %s.\n", __func__, MACB_POLL_VERSION); + + while (!kthread_should_stop()) { + if (!bp->link || bp->crc_detect_period == 0) { + ssleep(DEFAULT_SLEEP_S); + continue; + } + + pcs_link = macb_readl(bp, NSR) & MACB_BIT(NSR_LINK); + if (!pcs_link) { + netdev_info(bp->dev, "pcs reset.\n"); + macb_pcs_software_reset(bp, true); + } + + /* check speed and duplex of mac and phy */ + if (macb_check_link_speed_duplex(dev)) { + msleep(bp->crc_detect_period); + continue; + } + + reset_flag = false; + /* 1. detect num of detected rx_pkts + * 2. detect percent of crc error + * 3. phy reset + */ + macb_stats = macb_get_stats(dev); + if (!flag) { + rx_pkts_prev = macb_stats->rx_packets; + num_crc_err_prev = macb_stats->rx_crc_errors; + num_err_drop_total = macb_stats->rx_errors + macb_stats->rx_dropped; + flag = true; + } + + rx_pkts = macb_stats->rx_packets - rx_pkts_prev; + num_crc_err = macb_stats->rx_crc_errors - num_crc_err_prev; + rx_pkts_total = rx_pkts + num_crc_err; + if (rx_pkts_total >= bp->nb_rx && rx_pkts_total != 0) { + percent = (num_crc_err * 100) / rx_pkts_total; + if (percent >= bp->crc_err_percent) { + phy_reset(dev); + reset_flag = true; + bp->phy_reset_times++; + } + } + + rx_pkts_prev += rx_pkts; + num_crc_err_prev += num_crc_err; + + num_err_drop = macb_stats->rx_errors + macb_stats->rx_dropped - num_err_drop_total; + if (num_err_drop > rx_pkts) { + if (rx_error_count == 3) { + netdev_crit(dev, "reset serdes and device, num_err_drop:[%lu], rx_pkts:[%lu]\n", + num_err_drop, rx_pkts); + if (!reset_flag) { + phy_reset(dev); + bp->phy_reset_times++; + } + + if (!rtnl_trylock()) { + netdev_crit(dev, "%s rtnl_trylock failed\n", __func__); + num_err_drop_total += num_err_drop; + msleep(bp->crc_detect_period); + continue; + } + macb_suspend(device); + macb_resume(device); + rtnl_unlock(); + rx_error_count = 0; + } else { + rx_error_count++; + } + } else { + rx_error_count = 0; + } + + num_err_drop_total += num_err_drop; + + msleep(bp->crc_detect_period); + } + + phy_base_unmap(dev); + return 0; +} + static void macb_probe_queues(void __iomem *mem, bool native_io, unsigned int *queue_mask, @@ -4602,6 +5015,9 @@ static int macb_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); + bp->crc_detect_period = DEFAULT_CHECK_FCS_PERIOD_MS; + bp->nb_rx = DEFAULT_CHECK_FCS_RX_THRESHOLD; + bp->crc_err_percent = DEFAULT_CHECK_FCS_PERCENT_THRESHOLD; bp->pdev = pdev; bp->dev = dev; bp->regs = mem; @@ -4633,6 +5049,10 @@ static int macb_probe(struct platform_device *pdev) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); + err = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp); + if (err) + return err; + spin_lock_init(&bp->lock); /* setup capabilities */ @@ -4783,6 +5203,8 @@ static int macb_remove(struct platform_device *pdev) struct macb *bp; struct device_node *np = pdev->dev.of_node; + sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp); + dev = platform_get_drvdata(pdev); if (dev) { -- Gitee From c9fb1a71972057422b1c136b8c11fe58fdf4cb53 Mon Sep 17 00:00:00 2001 From: Wu Jinyong Date: Fri, 8 Nov 2024 15:03:54 +0800 Subject: [PATCH 39/41] reset:phytium: update the copyright information This patch updates the copyright infomation of phytium technology Signed-off-by: Wu Jinyong --- drivers/reset/Kconfig | 2 +- drivers/reset/reset-phytium.c | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 661e205dbf..1aa460db76 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -146,7 +146,7 @@ config RESET_TI_SYSCON config RESET_PHYTIUM bool "PHYTIUM Reset Driver" - default ARCH_iPHYTIUM + default ARCH_PHYTIUM help This enables the reset driver support for phytium devices with memory-mapped reset registers as part of a syscon device node. If diff --git a/drivers/reset/reset-phytium.c b/drivers/reset/reset-phytium.c index c77be1be75..fbefd6bc93 100644 --- a/drivers/reset/reset-phytium.c +++ b/drivers/reset/reset-phytium.c @@ -1,14 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Simple Reset Controller Driver + * Phytium Reset Controller Driver * - * Copyright (C) 2017 Pengutronix, Philipp Zabel - * - * Based on Allwinner SoCs Reset Controller driver - * - * Copyright 2013 Maxime Ripard - * - * Maxime Ripard + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. */ #include @@ -28,9 +22,9 @@ #define PHYTIUM_RESET_OFFSET_1 (4) struct reset_phytium_dev { - void __iomem *base; + void __iomem *base; struct device *dev; - struct reset_controller_dev rcdev; + struct reset_controller_dev rcdev; }; struct reset_reg_info { -- Gitee From 192502d976bf29018afcfef2ddc905926fda895e Mon Sep 17 00:00:00 2001 From: Jiakun Shuai Date: Wed, 20 Nov 2024 17:39:26 +0800 Subject: [PATCH 40/41] qspi: phytium: sync bmc and host qspi function Fix the function of qspi and now it support quad thread write, direct and indirect write mode. Signed-off-by: Peng Min Signed-off-by: Jiakun Shuai --- drivers/mtd/spi-nor/phytium-quadspi.c | 158 ++++++++++++-------------- 1 file changed, 72 insertions(+), 86 deletions(-) diff --git a/drivers/mtd/spi-nor/phytium-quadspi.c b/drivers/mtd/spi-nor/phytium-quadspi.c index 344b707f9f..847c263780 100644 --- a/drivers/mtd/spi-nor/phytium-quadspi.c +++ b/drivers/mtd/spi-nor/phytium-quadspi.c @@ -604,7 +604,72 @@ static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, return len; } -static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, +static ssize_t phytium_qspi_dir_write(struct spi_nor *nor, loff_t to, size_t len, + const u_char *buf) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = nor->program_opcode; + u32 addr = (u32)to; + u32 transfer = PHYTIUM_QSPI_1_1_1; + int i; + u_char tmp[8] = {0}; + size_t mask = 0x03; + + addr = addr + flash->cs * flash->fsize; + dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#zx\n", + nor->program_opcode, buf, addr, len); + + if (addr & 0x03) { + dev_err(dev, "Addr not four-byte aligned!\n"); + return -EINVAL; + } + + cmd = cmd << QSPI_WR_CFG_WR_CMD_SHIFT; + cmd |= BIT(QSPI_WR_CFG_WR_MODE_SHIFT); + cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + + cmd &= ~QSPI_WR_CFG_WR_TRANSFER_MASK; + transfer = phytium_qspi_get_transfer(nor->write_proto); + cmd |= transfer << QSPI_WR_CFG_WR_TRANSFER_SHIFT; + + switch (nor->program_opcode) { + case CMD_PP: + case CMD_QPP: + cmd &= ~QSPI_WR_CFG_WR_ADDR_SEL_MASK; + break; + case CMD_4PP: + case CMD_4QPP: + cmd |= BIT(QSPI_WR_CFG_WR_ADDR_SEL_SHIFT); + break; + default: + dev_err(qspi->dev, "Not support program command:%#x\n", + nor->erase_opcode); + return -EINVAL; + } + + dev_dbg(qspi->dev, "write cmd:%x\n", cmd); + writel_relaxed(cmd, qspi->io_base + QSPI_WR_CFG_REG); + + for (i = 0; i < len/4; i++) + writel_relaxed(*(u32 *)(buf + 4*i), qspi->mm_base + addr + 4*i); + + if (len & mask) { + addr = addr + (len & ~mask); + phytium_qspi_read_tmp(qspi, flash->read_cmd, addr, 4, &tmp[0]); + memcpy(tmp, buf + (len & ~mask), len & mask); + writel_relaxed(*(u32 *)(tmp), qspi->mm_base + addr); + } + + writel_relaxed(QSPI_FLUSH_EN, qspi->io_base + QSPI_FLUSH_REG); + + phytium_qspi_wait_cmd(qspi, flash); + + return len; +} + +static ssize_t phytium_qspi_nodir_write(struct spi_nor *nor, loff_t to, size_t len, const u_char *buf) { struct phytium_qspi_flash *flash = nor->priv; @@ -691,86 +756,6 @@ static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, return len; } -static ssize_t phytium_qspi_nodirmap_write(struct spi_nor *nor, loff_t to, size_t len, - const u_char *buf) -{ - struct phytium_qspi_flash *flash = nor->priv; - struct device *dev = flash->qspi->dev; - struct phytium_qspi *qspi = flash->qspi; - u32 cmd = nor->program_opcode; - u32 addr = (u32)to; - int i; - u_char tmp[8] = {0}; - size_t mask = 0x03; - size_t mask_p = 0x07; - u8 len_p; - - if (addr & 0x03) { - dev_err(dev, "Addr not four-byte aligned!\n"); - return -EINVAL; - } - - cmd = cmd << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; - cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); - cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); - cmd |= flash->clk_div & QSPI_CMD_PORT_SCK_SEL_MASK; - cmd |= 0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT; - - switch (nor->program_opcode) { - case CMD_PP: - case CMD_QPP: - cmd &= ~(0x1 << QSPI_CMD_PORT_SEL_SHIFT); - break; - case CMD_4PP: - case CMD_4QPP: - cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); - break; - default: - dev_err(qspi->dev, "Not support program command:%#x\n", - nor->erase_opcode); - return -EINVAL; - } - - for (i = 0; i < len/8; i++) { - phytium_qspi_write_enable(qspi, flash); - writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); - writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); - writel_relaxed(*(u32 *)(buf + 4 + 8 * i), - qspi->io_base + QSPI_HD_PORT_REG); - writel_relaxed(*(u32 *)(buf + 8 * i), - qspi->io_base + QSPI_LD_PORT_REG); - phytium_qspi_wait_cmd(qspi, flash); - phytium_qspi_write_disable(qspi, flash); - addr += 8; - } - - len_p = (u8)(len & mask_p); - if (len_p) { - phytium_qspi_write_enable(qspi, flash); - cmd &= ~(0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT); - cmd |= (len_p - 1) << QSPI_CMD_PORT_RW_NUM_SHIFT; - writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); - writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); - if ((len_p - 4) > 0) { - memcpy(tmp, buf + 4 + (len / 8) * 8, len_p - 4); - writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_HD_PORT_REG); - memcpy(tmp, buf + (len / 8) * 8, 4); - writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_LD_PORT_REG); - } else if (len_p == 4) { - memcpy(tmp, buf + (len / 8) * 8, 4); - writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_LD_PORT_REG); - } else { - memcpy(tmp, buf + (len / 8) * 8, len & mask); - writel_relaxed(*(u32 *)tmp, qspi->io_base + QSPI_LD_PORT_REG); - } - } - - phytium_qspi_wait_cmd(qspi, flash); - - return len; -} - static int phytium_qspi_erase(struct spi_nor *nor, loff_t offs) { struct phytium_qspi_flash *flash = nor->priv; @@ -946,8 +931,10 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, SNOR_HWCAPS_PP_4_4_4; } - if (of_property_read_bool(np, "dirmap-write")) + if (of_property_read_bool(np, "dirmap-write")) { + dev_warn(qspi->dev, "direct write mode!"); dirmap_write = true; + } flash = &qspi->flash[cs_num]; flash->qspi = qspi; @@ -963,9 +950,9 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, flash->nor.read = phytium_qspi_read; if (dirmap_write) - flash->nor.write = phytium_qspi_write; + flash->nor.write = phytium_qspi_dir_write; else - flash->nor.write = phytium_qspi_nodirmap_write; + flash->nor.write = phytium_qspi_nodir_write; flash->nor.erase = phytium_qspi_erase; flash->nor.read_reg = phytium_qspi_read_reg; flash->nor.write_reg = phytium_qspi_write_reg; @@ -989,9 +976,8 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, flash_cap = cs_num << QSPI_FLASH_CAP_NUM_SHIFT; flash_cap |= ret; + writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); - writel_relaxed(0x05000000, qspi->io_base + QSPI_WR_CFG_REG); - if (!dirmap_write) writel_relaxed(WR_CFG_NODIRMAP_VALUE, qspi->io_base + QSPI_WR_CFG_REG); -- Gitee From f73604cfcf6619ff40df12a1f9a97451f9ea10f9 Mon Sep 17 00:00:00 2001 From: Wang Hao Date: Thu, 5 Dec 2024 18:05:14 +0800 Subject: [PATCH 41/41] feature:add Phytium vram driver in dev-4.19 add phytium vram driver for test in kernel-4.19 Signed-off-by: Wang Hao --- .../bindings/media/phytium-jpeg-vram.txt | 27 + MAINTAINERS | 2 + arch/arm64/boot/dts/phytium/pe2201.dtsi | 14 + drivers/media/platform/Kconfig | 10 + drivers/media/platform/Makefile | 2 + drivers/media/platform/phytium-vram/Kconfig | 12 + drivers/media/platform/phytium-vram/Makefile | 3 + .../platform/phytium-vram/phytium_vram_core.c | 1425 +++++++++++++++++ .../platform/phytium-vram/phytium_vram_core.h | 168 ++ .../platform/phytium-vram/phytium_vram_reg.h | 157 ++ 10 files changed, 1820 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt create mode 100644 drivers/media/platform/phytium-vram/Kconfig create mode 100644 drivers/media/platform/phytium-vram/Makefile create mode 100644 drivers/media/platform/phytium-vram/phytium_vram_core.c create mode 100644 drivers/media/platform/phytium-vram/phytium_vram_core.h create mode 100644 drivers/media/platform/phytium-vram/phytium_vram_reg.h diff --git a/Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt b/Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt new file mode 100644 index 0000000000..787381e9e4 --- /dev/null +++ b/Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt @@ -0,0 +1,27 @@ +* Device tree bindings for Phytium VRAM Engine + +The VRAM Engine embedded in the Phytium SOCs can capture +and compress video data from digital or analog sources. + +Required properties: + - compatible: "phytium,jpeg-vram" + - reg: contains the offset and length of the + VRAM Engine memory region + - interrupts: the interrupt associated with the VE on this platform + - ddr-mode: storage the ddr-mode related register information + +Example: + +jpeg_vram0: jpeg_vram@32b32000 { + compatible = "phytium,jpeg-vram"; + reg = <0x0 0x32b32000 0 0x1000>, + <0x0 0x32000000 0 0x8000>; + interrupts = ; + status = "disabled"; + ddr-mode { + phytium,dc-reg = <0x32000000 0x2000>; + phytium,dc-vram = <0xf4000000 0x4000000>; + phytium,jpeg-vram = <0xf9000000 0x1000000>; + phytium,trans-reg = <0x32b3a000 0x1000>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 2564021626..6be91a3855 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1866,6 +1866,7 @@ F: Documentation/devicetree/bindings/ipmi/phytium,bt-bmc.txt F: Documentation/devicetree/bindings/ipmi/phytium,kcs-bmc.txt F: Documentation/devicetree/bindings/mailbox/phytium-mailbox.txt F: Documentation/devicetree/bindings/media/phytium-jpeg.txt +F: Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt F: Documentation/devicetree/bindings/mmc/phytium-mci.txt F: Documentation/devicetree/bindings/net/can/phytium-can.txt F: Documentation/devicetree/bindings/pci/phytium,phytium-pcie-ep.txt @@ -1892,6 +1893,7 @@ F: drivers/input/serio/phytium-ps2.c F: drivers/irqchip/irq-phytium-ixic.c F: drivers/mailbox/phytium_mailbox.c F: drivers/media/platform/phytium-jpeg/phytium_jpeg* +F: drivers/media/platform/phytium-jpeg/phytium_vram* F: drivers/mfd/phytium_px210_i2s_lsd.c F: drivers/mfd/phytium_px210_i2s_mmd.c F: drivers/mmc/host/phytium-mci* diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index 25885f6946..b880aeff86 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -434,6 +434,20 @@ jpeg0: jpeg@32b32000 { status = "disabled"; }; + jpeg_vram0: jpeg_vram@32b32000 { + compatible = "phytium,jpeg-vram"; + reg = <0x0 0x32b32000 0 0x1000>, + <0x0 0x32000000 0 0x8000>; + interrupts = ; + status = "disabled"; + ddr-mode { + phytium,dc-reg = <0x32000000 0x2000>; + phytium,dc-vram = <0xf4000000 0x4000000>; + phytium,jpeg-vram = <0xf9000000 0x1000000>; + phytium,trans-reg = <0x32b3a000 0x1000>; + }; + }; + jtag: jtag@28044000 { compatible = "phytium,jtag-master"; reg = <0x0 0x28044000 0x0 0x1000>; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6c224071bc..a3ffc680e9 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -151,6 +151,12 @@ config VIDEO_TI_CAL In TI Technical Reference Manual this module is referred as Camera Interface Subsystem (CAMSS). +choice + prompt "Phytium jpeg mode" + default VIDEO_PHYTIUM_JPEG_VRAM + help + phytium jpeg mode configuration. + config VIDEO_PHYTIUM_JPEG tristate "Phytium JPEG Encoder driver" depends on VIDEO_V4L2 @@ -161,6 +167,10 @@ config VIDEO_PHYTIUM_JPEG The engine can capture and compress video data from digital or analog sources. +source "drivers/media/platform/phytium-vram/Kconfig" + +endchoice + endif # V4L_PLATFORM_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index f89634e323..1999b6efbd 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -95,6 +95,8 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-$(CONFIG_VIDEO_PHYTIUM_JPEG) += phytium-jpeg/ +obj-$(CONFIG_VIDEO_PHYTIUM_JPEG_VRAM) += phytium-vram/ + obj-y += meson/ obj-y += cros-ec-cec/ diff --git a/drivers/media/platform/phytium-vram/Kconfig b/drivers/media/platform/phytium-vram/Kconfig new file mode 100644 index 0000000000..48c760482c --- /dev/null +++ b/drivers/media/platform/phytium-vram/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_PHYTIUM_JPEG_VRAM + tristate "Phytium JPEG VRAM Encoder driver" + depends on VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + help + Cannot be enabled together with VIDEO_PHYTIUM_JPEG ! + Support for the Phytium VRAM Encoder Engine embedded + in the Phytium SOCs. + The engine can capture and compress video data from + digital or analog sources. diff --git a/drivers/media/platform/phytium-vram/Makefile b/drivers/media/platform/phytium-vram/Makefile new file mode 100644 index 0000000000..57281b9d04 --- /dev/null +++ b/drivers/media/platform/phytium-vram/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +phytium_vram-objs := phytium_vram_core.o +obj-$(CONFIG_VIDEO_PHYTIUM_JPEG_VRAM) += phytium_vram.o diff --git a/drivers/media/platform/phytium-vram/phytium_vram_core.c b/drivers/media/platform/phytium-vram/phytium_vram_core.c new file mode 100644 index 0000000000..b913553e3f --- /dev/null +++ b/drivers/media/platform/phytium-vram/phytium_vram_core.c @@ -0,0 +1,1425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phytium VRAM Encoder Engine + * + * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include "phytium_vram_reg.h" +#include "phytium_vram_core.h" +#include + +#define BMC_MODE + +/* h_index(40) indicates high 8 bits of the height + * w_index(41) contains the low 8 bits of the height, + * and the width. For example, height 480(0x01e0) + * locates at 0x<01> 081100 and 0x038002 . + * width 640 (0x0280) locates at 0x03 <80> <02> e0. + */ + +/* color_mode(42) 0x0200 <11> 01 is a field marks + * YUV mode. + */ +static u32 phytium_jpeg_header[PHYTIUM_JPEG_HEADER_SIZE] = { + 0xe0ffd8ff, 0x464a0100, 0x01004649, 0x01000001, + 0x00000100, 0x4300dbff, 0x0c0b1000, 0x100a0c0e, + 0x120e0d0e, 0x18131011, 0x16181a28, 0x23311816, + 0x3a281d25, 0x393c3d33, 0x40373833, 0x404e5c48, + 0x37455744, 0x516d5038, 0x67625f57, 0x4d3e6768, + 0x64707971, 0x67655c78, 0x00dbff63, 0x12110143, + 0x18151812, 0x2f1a1a2f, 0x42384263, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0xc0ff6363, + 0x01081100, 0x038002e0, 0x02001101, 0x11030111, + 0x00c4ff01, 0x0100001f, 0x01010105, 0x00010101, + 0x00000000, 0x01000000, 0x05040302, 0x09080706, + 0xc4ff0b0a, 0x00011f00, 0x01010103, 0x01010101, + 0x00000101, 0x00000000, 0x04030201, 0x08070605, + 0xff0b0a09, 0x10b500c4, 0x03010200, 0x03040203, + 0x04040505, 0x7d010000, 0x00030201, 0x12051104, + 0x06413121, 0x07615113, 0x32147122, 0x08a19181, + 0xc1b14223, 0xf0d15215, 0x72623324, 0x160a0982, + 0x1a191817, 0x28272625, 0x35342a29, 0x39383736, + 0x4544433a, 0x49484746, 0x5554534a, 0x59585756, + 0x6564635a, 0x69686766, 0x7574736a, 0x79787776, + 0x8584837a, 0x89888786, 0x9493928a, 0x98979695, + 0xa3a29a99, 0xa7a6a5a4, 0xb2aaa9a8, 0xb6b5b4b3, + 0xbab9b8b7, 0xc5c4c3c2, 0xc9c8c7c6, 0xd4d3d2ca, + 0xd8d7d6d5, 0xe2e1dad9, 0xe6e5e4e3, 0xeae9e8e7, + 0xf4f3f2f1, 0xf8f7f6f5, 0xc4fffaf9, 0x0011b500, + 0x04020102, 0x07040304, 0x00040405, 0x00770201, + 0x11030201, 0x31210504, 0x51411206, 0x13716107, + 0x08813222, 0xa1914214, 0x2309c1b1, 0x15f05233, + 0x0ad17262, 0xe1342416, 0x1817f125, 0x27261a19, + 0x352a2928, 0x39383736, 0x4544433a, 0x49484746, + 0x5554534a, 0x59585756, 0x6564635a, 0x69686766, + 0x7574736a, 0x79787776, 0x8483827a, 0x88878685, + 0x93928a89, 0x97969594, 0xa29a9998, 0xa6a5a4a3, + 0xaaa9a8a7, 0xb5b4b3b2, 0xb9b8b7b6, 0xc4c3c2ba, + 0xc8c7c6c5, 0xd3d2cac9, 0xd7d6d5d4, 0xe2dad9d8, + 0xe6e5e4e3, 0xeae9e8e7, 0xf5f4f3f2, 0xf9f8f7f6, + 0x00fefffa, 0x0000008f, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdaff0000, 0x01030c00, 0x03110200, 0x003f0011 +}; + +static char yuv_mode_str[YUV_MODE_STR_LEN] = { "yuv444" }; +static int dc_addr; + + +module_param_string(yuv_mode, yuv_mode_str, sizeof(yuv_mode_str), 0444); +module_param(dc_addr, int, 0644); + + +MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as\n" + " \t\t'yuv444', or 'yuv422', or 'yuv420'. If no mode is set,\n" + " \t\tthe driver adapts defaults mode 'yuv444'."); + +static void phytium_jpeg_init_jpeg_quant(struct phytium_jpeg_dev *jpeg_dev); +static void phytium_jpeg_set_yuv_mode(struct phytium_jpeg_dev *jpeg_dev); + +static u32 phytium_jpeg_read(struct phytium_jpeg_dev *jpeg_dev, u32 reg) +{ + u32 reg_val = readl(jpeg_dev->base_addr + reg); + + dev_dbg(jpeg_dev->dev, "read 0x%p + 0x%x -->val[0x%x]\n", + jpeg_dev->base_addr, reg, reg_val); + + return reg_val; +} + +static void phytium_jpeg_write(struct phytium_jpeg_dev *jpeg_dev, + u32 reg, u32 val) +{ + writel(val, jpeg_dev->base_addr + reg); + dev_dbg(jpeg_dev->dev, "write 0x%x to addr 0x%p + 0x%x\n", + val, jpeg_dev->base_addr, reg); +} + +static void phytium_jpeg_update(struct phytium_jpeg_dev *jpeg_dev, u32 reg, + u32 clear, u32 bits) +{ + u32 reg_val = readl(jpeg_dev->base_addr + reg); + u32 tmp = reg_val; + + reg_val &= ~clear; + reg_val |= bits; + writel(reg_val, jpeg_dev->base_addr + reg); + + dev_dbg(jpeg_dev->dev, "the val of addr 0x%p + 0x%x, from 0x%x to 0x%x\n", + jpeg_dev->base_addr, reg, tmp, readl(jpeg_dev->base_addr + reg)); +} + +static void phytium_jpeg_init_regs(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 transform_info = 0; + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + u32 rate_to_reg = 0; + + spin_lock_irq(&jpeg_dev->reset_lock); + /* First, disable the JPEG engine, set bit0 = 0*/ + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, transform_info); + + /* Second, set VGAvideo_source_information. bit1 = 0 marks VGA */ + transform_info |= 0; + + /* Third, set AXI burst length bit[16:22]= 0xf , default value*/ + transform_info |= (0xF << TRANS_AXI_LEN_SHIFT) & TRANSINFO_AXI_LEN; + + /* Fourth, the default sampling format is YUV422, set bit13 to 0 */ + /* ignore setting sampling interval */ + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, transform_info); + udelay(5); + + /* Fifth, setting frame rate. + * Linux driver prohibit float point operations. So use the + * format: reg_val = (1 second * 10^8 / frame_rate / 134 *100) + * write reg_val to register. then enable Highest bit31 = 1 + */ + if (jpeg_dev->frame_rate) { + rate_to_reg = 100000000 / jpeg_dev->frame_rate / 134 * 100; + rate_to_reg |= FRAME_SAMPLE_CTRL_EN; + phytium_jpeg_write(jpeg_dev, FRAME_SAMPLE_CTRL, rate_to_reg); + } + /* Sixth, HUFF_MODE, driver needn't to configure, ignore */ + + /* disable all interrupts and then clear all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, + disable_all_interrupt); + udelay(5); + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + spin_unlock_irq(&jpeg_dev->reset_lock); + + /* Seventh, Sample_mode, hardware default is yuv444 */ + jpeg_dev->yuv420 = false; +} + +static void phytium_jpeg_reset_init(struct phytium_jpeg_dev *jpeg_dev) +{ + struct arm_smccc_res res; + + spin_lock_irq(&jpeg_dev->reset_lock); + /* call SE to poweroff JPEG Engine */ + arm_smccc_smc(PHYTIUM_SET_OCN_JPEG_OFF, 0x9, 0x3, 0x80000020, 0, 0, 0, 0, &res); + spin_unlock_irq(&jpeg_dev->reset_lock); + + phytium_jpeg_init_jpeg_quant(jpeg_dev); + + /* Select YUV mode */ + phytium_jpeg_set_yuv_mode(jpeg_dev); + phytium_jpeg_init_regs(jpeg_dev); +} + +/* Disable the jpeg engine */ +static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + + spin_lock_irq(&jpeg_dev->reset_lock); + /* disable all interrupt */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); + /* clear all interrupt */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + /* disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + spin_unlock_irq(&jpeg_dev->reset_lock); + + /* wait 50 ms */ + mdelay(50); + /* C08 bit7 1:busy */ +} + + +#define res_check(val) \ + test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(val)->status) + +/* The below functions is implemented for various v4l2 ioctl operations */ +static int phytium_jpeg_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + strscpy(cap->driver, PHYTIUM_JPEG_NAME, sizeof(cap->driver)); + strscpy(cap->card, "Phytium JPEG Engine", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(jpeg_dev->dev)); + + return 0; +} + +static int phytium_jpeg_enum_format(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "1\n"); + + if (f->index) { + dev_err(jpeg_dev->dev, "Failed to enum format\n"); + return -EINVAL; + } + + f->pixelformat = V4L2_PIX_FMT_JPEG; + + return 0; +} + +static int phytium_jpeg_get_format(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + f->fmt.pix = jpeg_dev->pix_fmt; + + dev_dbg(jpeg_dev->dev, "2\n"); + + return 0; +} + +static int phytium_jpeg_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "3\n"); + + if (input->index) { + dev_err(jpeg_dev->dev, "failed to enum input\n"); + return -EINVAL; + } + + strscpy(input->name, "Host DC Capture", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->capabilities = V4L2_IN_CAP_DV_TIMINGS; + input->status = jpeg_dev->v4l2_input_status; + + return 0; +} + +static int phytium_jpeg_get_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int phytium_jpeg_set_input(struct file *file, void *priv, + unsigned int i) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (i != 0) { + dev_err(jpeg_dev->dev, "Failed to set input\n"); + return -EINVAL; + } + + return 0; +} + +static int phytium_jpeg_get_parm(struct file *file, void *priv, + struct v4l2_streamparm *stream) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + /* Readbuffers num is 3 */ + stream->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + stream->parm.capture.readbuffers = CAPTURE_BUF_NUMBER; + stream->parm.capture.timeperframe.denominator = 1; + + if (jpeg_dev->frame_rate == 0) + stream->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; + else + stream->parm.capture.timeperframe.denominator = jpeg_dev->frame_rate; + + return 0; +} + +static int phytium_jpeg_set_parm(struct file *file, void *priv, + struct v4l2_streamparm *stream) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + unsigned int frame_rate = 0; + + stream->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + stream->parm.capture.readbuffers = CAPTURE_BUF_NUMBER; + + if (stream->parm.capture.timeperframe.numerator) + frame_rate = stream->parm.capture.timeperframe.denominator / + stream->parm.capture.timeperframe.numerator; + + if (frame_rate == 0 || frame_rate > MAX_FRAME_RATE) { + frame_rate = MAX_FRAME_RATE; + stream->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; + stream->parm.capture.timeperframe.numerator = 1; + } + + jpeg_dev->frame_rate = frame_rate; + + return 0; +} + +static int phytium_jpeg_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) + +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "4\n"); + + if (fsize->index != 0) { + dev_err(jpeg_dev->dev, "Failed to enum framesize.\n"); + return -EINVAL; + } + + if (fsize->pixel_format != V4L2_PIX_FMT_JPEG) { + dev_err(jpeg_dev->dev, "enum framesize pixel_format is not JPEG"); + return -EINVAL; + } + + fsize->discrete.width = jpeg_dev->pix_fmt.width; + fsize->discrete.height = jpeg_dev->pix_fmt.height; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; + +} + +static int phytium_jpeg_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "5\n"); + + if (fival->index != 0) { + dev_err(jpeg_dev->dev, "enum frame intervals failed\n"); + return -EINVAL; + } + + if (fival->width != jpeg_dev->detected_timings.width || + fival->height != jpeg_dev->detected_timings.height) { + dev_err(jpeg_dev->dev, "interval isn't same with the detected_timings.\n"); + return -EINVAL; + } + + if (fival->pixel_format != V4L2_PIX_FMT_JPEG) { + dev_err(jpeg_dev->dev, "enum frame interval pixel fomat is incorrect.\n"); + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fival->stepwise.min.denominator = MAX_FRAME_RATE; + fival->stepwise.min.numerator = 1; + fival->stepwise.max.denominator = 1; + fival->stepwise.max.numerator = 1; + fival->stepwise.step = fival->stepwise.max; + + return 0; +} + +static int phytium_jpeg_set_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "6\n"); + + /* the params are passed from user space are same with hardware's params */ + if (timings->bt.width == jpeg_dev->active_timings.width && + timings->bt.height == jpeg_dev->active_timings.height) + return 0; + + if (vb2_is_busy(&jpeg_dev->queue)) { + dev_err(jpeg_dev->dev, "queue is busy during setting dv timings.\n"); + return -EBUSY; + } + + jpeg_dev->active_timings = timings->bt; + jpeg_dev->max_compressed_size = + jpeg_dev->active_timings.width * jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.width = timings->bt.width; + jpeg_dev->pix_fmt.height = timings->bt.height; + jpeg_dev->pix_fmt.sizeimage = jpeg_dev->max_compressed_size; + timings->type = V4L2_DV_BT_656_1120; + + return 0; +} + +static int phytium_jpeg_get_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + timings->type = V4L2_DV_BT_656_1120; + timings->bt = jpeg_dev->active_timings; + dev_dbg(jpeg_dev->dev, "7 %d %d\n", jpeg_dev->detected_timings.width, + jpeg_dev->detected_timings.height); + + return 0; +} + +static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) + msleep(100); + + timings->type = V4L2_DV_BT_656_1120; + timings->bt = jpeg_dev->detected_timings; + dev_dbg(jpeg_dev->dev, "8 %d %d\n", jpeg_dev->detected_timings.width, + jpeg_dev->detected_timings.height); + + return jpeg_dev->v4l2_input_status ? -ENOLINK : 0; +} + +static const struct v4l2_dv_timings_cap phytium_jpeg_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + .min_pixelclock = 6574080, /* 640 x 480 x 24Hz */ + .max_pixelclock = 1244160000, /* 1920 x 1080 x 60Hz */ + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static int phytium_jpeg_enum_dv_timings(struct file *file, void *priv, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &phytium_jpeg_timings_cap, + NULL, NULL); +} + +static int phytium_jpeg_dv_timings_cap(struct file *file, void *priv, + struct v4l2_dv_timings_cap *cap) +{ + *cap = phytium_jpeg_timings_cap; + + return 0; +} + +/* The function is used to notify DV that video resolution is altered */ +static int phytium_jpeg_sub_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + default: + break; + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops phytium_jpeg_ioctl_ops = { + .vidioc_querycap = phytium_jpeg_querycap, + .vidioc_enum_fmt_vid_cap = phytium_jpeg_enum_format, + .vidioc_g_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_s_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_try_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = phytium_jpeg_enum_input, + .vidioc_g_input = phytium_jpeg_get_input, + .vidioc_s_input = phytium_jpeg_set_input, + .vidioc_g_parm = phytium_jpeg_get_parm, + .vidioc_s_parm = phytium_jpeg_set_parm, + .vidioc_enum_framesizes = phytium_jpeg_enum_framesizes, + .vidioc_enum_frameintervals = phytium_jpeg_enum_frameintervals, + .vidioc_s_dv_timings = phytium_jpeg_set_dv_timings, + .vidioc_g_dv_timings = phytium_jpeg_get_dv_timings, + .vidioc_query_dv_timings = phytium_jpeg_query_dv_timings, + .vidioc_enum_dv_timings = phytium_jpeg_enum_dv_timings, + .vidioc_dv_timings_cap = phytium_jpeg_dv_timings_cap, + .vidioc_subscribe_event = phytium_jpeg_sub_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void phytium_jpeg_init_jpeg_quant(struct phytium_jpeg_dev *jpeg_dev) +{ + const u32 y_quant_table[QUANT_REG_NUM] = { + 0x08000000, 0x0ba2e8ba, 0x0aaaaaab, 0x09249249, 0x0aaaaaab, + 0x0ccccccc, 0x08000000, 0x09249249, 0x09d89d8a, 0x09249249, + 0x071c71c7, 0x07878788, 0x08000000, 0x06bca1af, 0x05555555, + 0x03333333, 0x04ec4ec5, 0x05555555, 0x05d1745d, 0x05d1745d, + 0x05555555, 0x029cbc15, 0x03a83a84, 0x03759f23, 0x0469ee58, + 0x03333333, 0x0234f72c, 0x02828283, 0x02192e2a, 0x02222222, + 0x023ee090, 0x02828283, 0x02492492, 0x0253c825, 0x02000000, + 0x01c71c72, 0x01642c86, 0x01a41a42, 0x02000000, 0x01e1e1e2, + 0x0178a4c8, 0x01dae607, 0x0253c825, 0x02492492, 0x0199999a, + 0x012c9fb5, 0x01948b10, 0x0178a4c8, 0x0158ed23, 0x014e5e0a, + 0x013e22cc, 0x013b13b1, 0x013e22cc, 0x02108421, 0x01a98ef6, + 0x0121fb78, 0x010ecf57, 0x01249249, 0x013e22cc, 0x01111111, + 0x01642c86, 0x01446f86, 0x013e22cc, 0x014afd6a + }; + + const u32 c_quant_table[QUANT_REG_NUM] = { + 0x07878788, 0x071c71c7, 0x071c71c7, 0x05555555, 0x06186186, + 0x05555555, 0x02b93105, 0x04ec4ec5, 0x04ec4ec5, 0x02b93105, + 0x014afd6a, 0x01f07c1f, 0x02492492, 0x01f07c1f, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a + }; + int i; + + spin_lock_irq(&jpeg_dev->reset_lock); + for (i = 0; i < QUANT_REG_NUM; i++) { + phytium_jpeg_write(jpeg_dev, Y_QUANT_INDEX_ADDR_REG(i), y_quant_table[i]); + phytium_jpeg_write(jpeg_dev, C_QUANT_INDEX_ADDR_REG(i), c_quant_table[i]); + } + spin_unlock_irq(&jpeg_dev->reset_lock); +} + +static void phytium_jpeg_start(struct phytium_jpeg_dev *jpeg_dev) +{ + int i, value; + char *dst, *src; + unsigned long trans_src; + unsigned long dma_addr; + struct phytium_dc_param dc_param; + void __iomem *dc_reg = jpeg_dev->dc_reg; + + jpeg_dev->v4l2_input_status = 0; + + value = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if ((value & FRAMEBUFFER_OUTPUT) && (!(value & FRAMEBUFFER_CLEAR))) { + dc_param.width = readl(dc_reg + PHYTIUM_DC_HDISPLAY) & HDISPLAY_END_MASK; + dc_param.height = readl(dc_reg + PHYTIUM_DC_VDISPLAY) & VDISPLAY_END_MASK; + dc_param.stride = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE); + dma_addr = (readl(dc_reg + PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS) & PREFIX_MASK); + dma_addr = (dma_addr << PREFIX_SHIFT); + dma_addr |= readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS); + dc_param.dma_addr = dma_addr; + jpeg_dev->dc_param = dc_param; +#ifdef BMC_MODE + trans_src = readl(jpeg_dev->trans_reg + PE220X_DC_ADDR_TRANS_SRC_ADDR); + trans_src = trans_src << ADDR_OFFSET; +#else + trans_src = jpeg_dev->dc_vram_info.dma_addr; +#endif + if (jpeg_dev->dc_param.dma_addr < trans_src) + src = jpeg_dev->dc_vram_info.virt_addr; + else + src = jpeg_dev->dc_vram_info.virt_addr + + (jpeg_dev->dc_param.dma_addr - trans_src); + + dst = jpeg_dev->src_addrs[0].virt_addr; + dev_dbg(jpeg_dev->dev, "23: dma_addr: 0x%lx trans_src:0x%lx\n", + jpeg_dev->dc_param.dma_addr, trans_src); + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memcpy(dst, src, jpeg_dev->dc_param.width * 4); + src += jpeg_dev->dc_param.stride; + dst += jpeg_dev->dc_param.width * 4; + } + } else { + jpeg_dev->dc_param.width = MIN_WIDTH; + jpeg_dev->dc_param.height = MIN_HEIGHT; + jpeg_dev->dc_param.stride = MIN_WIDTH * 4; + jpeg_dev->dc_param.dma_addr = 0; + dst = jpeg_dev->src_addrs[0].virt_addr; + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memset(dst, 0, jpeg_dev->dc_param.width * 4); + dst += jpeg_dev->dc_param.width * 4; + } + } + + jpeg_dev->detected_timings.width = jpeg_dev->dc_param.width; + jpeg_dev->detected_timings.height = jpeg_dev->dc_param.height; + jpeg_dev->active_timings = jpeg_dev->detected_timings; + jpeg_dev->max_compressed_size = jpeg_dev->active_timings.width + * jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.width = jpeg_dev->active_timings.width; + jpeg_dev->pix_fmt.height = jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.sizeimage = jpeg_dev->max_compressed_size; + +} + +static void phytium_jpeg_stop(struct phytium_jpeg_dev *jpeg_dev) +{ + phytium_jpeg_off(jpeg_dev); + + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + jpeg_dev->status = 0; +} + +static int phytium_jpeg_open(struct file *file) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + mutex_lock(&jpeg_dev->video_lock); + ret = v4l2_fh_open(file); + if (ret != 0) { + mutex_unlock(&jpeg_dev->video_lock); + dev_err(jpeg_dev->dev, "Failed to open the phytium jpeg device.\n"); + return ret; + } + + if (v4l2_fh_is_singular_file(file)) + phytium_jpeg_start(jpeg_dev); + + mutex_unlock(&jpeg_dev->video_lock); + + return 0; +} + +static int phytium_jpeg_release(struct file *file) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + mutex_lock(&jpeg_dev->video_lock); + + if (v4l2_fh_is_singular_file(file)) + phytium_jpeg_stop(jpeg_dev); + + ret = _vb2_fop_release(file, NULL); + mutex_unlock(&jpeg_dev->video_lock); + + return ret; +} + + +static const struct v4l2_file_operations phytium_jpeg_fops = { + .owner = THIS_MODULE, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = phytium_jpeg_open, + .release = phytium_jpeg_release, +}; + +static void phytium_jpeg_update_jpeg_header(u32 width, u32 height) +{ + const int h_index = PHYTIUM_JPEG_HEADER_H_INDEX; + const int w_index = PHYTIUM_JPEG_HEADER_W_INDEX; + + /* the high 8 bits of the height locates at bit24~bit31 */ + phytium_jpeg_header[h_index] = phytium_jpeg_header[h_index] & 0x00FFFFFF; + phytium_jpeg_header[h_index] |= ((height >> 8) & 0xFF) << 24; + + /* the low 8 bits of the height locates at bit0~bit7 */ + phytium_jpeg_header[w_index] = phytium_jpeg_header[w_index] & 0xFF000000; + phytium_jpeg_header[w_index] |= height & 0xFF; + + /* the high 8 bits of the width locates at bit8~bit15 */ + phytium_jpeg_header[w_index] |= ((width >> 8) & 0xFF) << 8; + /* the low 8 bits of the width locates at bit16~bit24 */ + phytium_jpeg_header[w_index] |= (width & 0xFF) << 16; +} + +static void phytium_jpeg_fill_header(struct phytium_jpeg_dev *jpeg_dev, + struct phytium_jpeg_buffer *jpeg_buf) +{ + void *vbuf = vb2_plane_vaddr(&jpeg_buf->vb.vb2_buf, 0); + u32 width = jpeg_dev->active_timings.width; + u32 height = jpeg_dev->active_timings.height; + + /* update the contents of the phytium jpeg header according to the resolution */ + phytium_jpeg_update_jpeg_header(width, height); + + /* replenish the contents of the JPEG header */ + memcpy(vbuf, phytium_jpeg_header, PHYTIUM_JPEG_HEADER_LEN); +} + +static int phytium_jpeg_start_frame(struct phytium_jpeg_dev *jpeg_dev) +{ + dma_addr_t dst_addr; + unsigned long status; + struct phytium_jpeg_buffer *jpeg_buf; + int src_addrs[OCM_BUF_NUM], i, value; + + if (jpeg_dev->v4l2_input_status) { + dev_err(jpeg_dev->dev, "No signal; needn't start frame\n"); + return 0; + } + + spin_lock_irqsave(&jpeg_dev->hw_lock, status); + jpeg_buf = list_first_entry_or_null(&jpeg_dev->buffers, + struct phytium_jpeg_buffer, link); + if (jpeg_buf == NULL) { + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + dev_err(jpeg_dev->dev, "No buffers; doesn't start frame\n"); + return -EPROTO; + } + + set_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + dst_addr = vb2_dma_contig_plane_dma_addr(&jpeg_buf->vb.vb2_buf, 0); + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + + /* + * Because the JPEG Engine is unable to add a JPEG header, the phytium + * jpeg driver is required to fill the contents of a JPEG header before + * the jpeg engine write datas to the dma address. + */ + phytium_jpeg_fill_header(jpeg_dev, jpeg_buf); + dst_addr += PHYTIUM_JPEG_HEADER_LEN; + /* + * The ikvm application only using the last frame, so the driver replenish + * one output register with a dma address. + */ + dst_addr >>= JPEG_DST_ADDR_SHIFT; + + for (i = 0; i < OCM_BUF_NUM; i++) + src_addrs[i] = jpeg_dev->src_addrs[i].dma_addr >> OCM_BUF_SHIFT; + + value = (((jpeg_dev->active_timings.width - 1) & CFG_H_MASK) << CFG_H_SHIFT); + value |= (((jpeg_dev->active_timings.height - 1) & CFG_V_MASK) << CFG_V_SHIFT); + value |= (CFG_IMG | CFG_OCM_VALID); + + spin_lock_irq(&jpeg_dev->reset_lock); + phytium_jpeg_write(jpeg_dev, BUF_LIST_INDEX_ADDR(VB_BUF_NO), dst_addr); + /* Enable the validilty of the buffer marked with index */ + phytium_jpeg_write(jpeg_dev, BUF_LIST_INDEX_CTRL_STS_ADDR(VB_BUF_NO), + STS_JPEG_BUF_HIGH_LEVEL_VALID); + phytium_jpeg_write(jpeg_dev, OCM_BUF0_ADDR, src_addrs[0]); + phytium_jpeg_write(jpeg_dev, OCM_BUF1_ADDR, src_addrs[0]); + + /* configure ddr mode w*h */ + phytium_jpeg_write(jpeg_dev, SRC_DDR_INFO_REG, value); + + /* Enable the interruption which is used to identify an image was compressed */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, 0, STS_VE_JPEG_CODE_COMP_EN); + + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, + TRANSINFO_IMAGE_STORE, TRANSINFO_IMAGE_STORE); + + /* Enable JPEG, start to capture and compress */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 1); + spin_unlock_irq(&jpeg_dev->reset_lock); + + return 0; +} + +static int phytium_jpeg_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + if (*num_planes) { + if (sizes[0] < jpeg_dev->max_compressed_size) { + v4l2_err(&jpeg_dev->v4l2_dev, "queue v4l2_buf's size is invalid\n"); + return -EINVAL; + } + } + + *num_planes = 1; + sizes[0] = jpeg_dev->max_compressed_size; + return 0; +} + +static int phytium_jpeg_buf_prepare(struct vb2_buffer *vb) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(vb->vb2_queue); + + if (vb2_plane_size(vb, 0) < jpeg_dev->max_compressed_size) { + v4l2_err(&jpeg_dev->v4l2_dev, "failed to prepare buffer\n"); + return -EINVAL; + } + + return 0; +} + +static inline struct phytium_jpeg_buffer * +phytium_vb2buf_to_dstbuf(struct vb2_v4l2_buffer *buf) +{ + return container_of(buf, struct phytium_jpeg_buffer, vb); +} + +static void phytium_jpeg_buf_queue(struct vb2_buffer *vb) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct phytium_jpeg_buffer *jpeg_buf = phytium_vb2buf_to_dstbuf(vbuf); + unsigned long status; + + spin_lock_irqsave(&jpeg_dev->hw_lock, status); + list_add_tail(&jpeg_buf->link, &jpeg_dev->buffers); + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); +} + +static void phytium_jpeg_bufs_done(struct phytium_jpeg_dev *jpeg_dev, + enum vb2_buffer_state state) +{ + unsigned long flags; + struct phytium_jpeg_buffer *buf; + + spin_lock_irqsave(&jpeg_dev->hw_lock, flags); + + list_for_each_entry(buf, &jpeg_dev->buffers, link) + vb2_buffer_done(&buf->vb.vb2_buf, state); + + INIT_LIST_HEAD(&jpeg_dev->buffers); + + spin_unlock_irqrestore(&jpeg_dev->hw_lock, flags); +} + +static irqreturn_t phytium_jpeg_irq(int irq, void *arg) +{ + struct phytium_jpeg_dev *jpeg_dev = arg; + u32 status; + struct phytium_jpeg_buffer *buf; + u32 frame_size; + + spin_lock_irq(&jpeg_dev->reset_lock); + status = phytium_jpeg_read(jpeg_dev, INT_STATUS_CTRL_REG); + spin_unlock_irq(&jpeg_dev->reset_lock); + + /* + * JPEG engine finish compressing a image JPEG encoding to trigger + * a interruption. the status identifies the buffer number. Currently, + * the driver uses one buffer. + * + * Note: Because the JPEG doesn't support adding a JPEG header, and + * driver is also unable to add a JPEG header to vb2_buffers. One + * solution is that a JPEG header is added by an application. + */ + if (status & INT_JPEG_COMP_BUF_LIST_NO) { + frame_size = phytium_jpeg_read(jpeg_dev, jpeg_dev->comp_size_read); + frame_size &= JPEG_BUF_CAPACITY_SIZE; + frame_size >>= JPEG_BUF_CAPACITY_SIZE_SHIFT; + spin_lock(&jpeg_dev->hw_lock); + if (test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) + clear_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); + + clear_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + /* Delete first node from the queue */ + buf = list_first_entry_or_null(&jpeg_dev->buffers, + struct phytium_jpeg_buffer, link); + if (buf != NULL) { + frame_size += PHYTIUM_JPEG_HEADER_LEN; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size); + if (!list_is_last(&buf->link, &jpeg_dev->buffers)) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = jpeg_dev->sequence++; + buf->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + list_del(&buf->link); + } + } + + spin_unlock(&jpeg_dev->hw_lock); + + spin_lock_irq(&jpeg_dev->reset_lock); + /* Disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* clear all interruption of the hardware's buffers */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, INT_JPEG_ENCODE_COMPLETE, 1); + spin_unlock_irq(&jpeg_dev->reset_lock); + wake_up_interruptible_all(&jpeg_dev->wait_update_ddr); + } + + return IRQ_HANDLED; +} + +/* VIDIOC_STREAMON, all vb2_v4l2_buf' states are queue */ +static int phytium_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + dev_dbg(jpeg_dev->dev, "9\n"); + + jpeg_dev->sequence = 0; + jpeg_dev->timeout_jiffies = 0; + phytium_jpeg_reset_init(jpeg_dev); + dev_dbg(jpeg_dev->dev, "15\n"); + ret = phytium_jpeg_start_frame(jpeg_dev); + if (ret != 0) { + phytium_jpeg_bufs_done(jpeg_dev, VB2_BUF_STATE_QUEUED); + return ret; + } + + /* set the states of the jpeg engine */ + set_bit(VIDEO_STREAMING, &jpeg_dev->status); + dev_dbg(jpeg_dev->dev, "16\n"); + return ret; +} + +static void phytium_jpeg_stop_streaming(struct vb2_queue *q) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + dev_dbg(jpeg_dev->dev, "10\n"); + + clear_bit(VIDEO_STREAMING, &jpeg_dev->status); + phytium_jpeg_off(jpeg_dev); + /* first stop jpeg, wait, the free buffer */ + phytium_jpeg_bufs_done(jpeg_dev, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops phytium_jpeg_vb2_ops = { + .queue_setup = phytium_jpeg_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = phytium_jpeg_buf_prepare, + .buf_queue = phytium_jpeg_buf_queue, + .start_streaming = phytium_jpeg_start_streaming, + .stop_streaming = phytium_jpeg_stop_streaming, +}; + +static void phytium_jpeg_get_yuv_mode(struct phytium_jpeg_dev *jpeg_dev) +{ + const char *mode = yuv_mode_str; + + if (strstr(mode, "yuv422") != NULL) + jpeg_dev->yuv_mode = YUV422; + else if (strstr(mode, "yuv420") != NULL) + jpeg_dev->yuv_mode = YUV420; + else + jpeg_dev->yuv_mode = YUV444; + + /* update the field which indicates YUV mode locates in the JPEG header. */ + phytium_jpeg_header[YUVID] &= 0xFFFF00FF; + if (jpeg_dev->yuv_mode == YUV422) + phytium_jpeg_header[YUVID] |= 0x2100; + else if (jpeg_dev->yuv_mode == YUV420) + phytium_jpeg_header[YUVID] |= 0x2200; + else + phytium_jpeg_header[YUVID] |= 0x1100; +} + +static void phytium_jpeg_set_yuv_mode(struct phytium_jpeg_dev *jpeg_dev) +{ + spin_lock_irq(&jpeg_dev->reset_lock); + /* set the yuv mode register */ + phytium_jpeg_write(jpeg_dev, SAMPLE_MODE_REG, jpeg_dev->yuv_mode); + spin_unlock_irq(&jpeg_dev->reset_lock); +} + +int phytium_jpeg_ddr_mode_thread(void *data) +{ + struct phytium_jpeg_dev *jpeg_dev = data; + char *src = NULL, *dst; + int i, ret, value; + unsigned long trans_src; + unsigned long dma_addr; + struct phytium_dc_param dc_param; + void __iomem *dc_reg; + struct v4l2_bt_timings *detected_timings = &jpeg_dev->detected_timings; + static const struct v4l2_event event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + dc_reg = jpeg_dev->dc_reg; + + for (;;) { + dev_dbg(jpeg_dev->dev, "11 0x%lx\n", jpeg_dev->status); + ret = wait_event_interruptible(jpeg_dev->wait_update_ddr, + (((!test_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status)) && + (!test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) && + test_bit(VIDEO_STREAMING, &jpeg_dev->status)) + || kthread_should_stop())); + if (ret) + dev_err(jpeg_dev->dev, "Failed to get ddr update event\n"); + + dev_dbg(jpeg_dev->dev, "12 0x%lx\n", jpeg_dev->status); + if (kthread_should_stop()) + break; + + dev_dbg(jpeg_dev->dev, "20\n"); + + value = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if ((value & FRAMEBUFFER_OUTPUT) && (!(value & FRAMEBUFFER_CLEAR))) { + dc_param.width = readl(dc_reg + PHYTIUM_DC_HDISPLAY) & HDISPLAY_END_MASK; + dc_param.height = readl(dc_reg + PHYTIUM_DC_VDISPLAY) & VDISPLAY_END_MASK; + dc_param.stride = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE); + dma_addr = (readl(dc_reg + PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS) + & PREFIX_MASK); + dma_addr = (dma_addr << PREFIX_SHIFT); + dma_addr |= readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS); + dc_param.dma_addr = dma_addr; + jpeg_dev->dc_param = dc_param; +#ifdef BMC_MODE + + trans_src = readl(jpeg_dev->trans_reg + PE220X_DC_ADDR_TRANS_SRC_ADDR); + trans_src = trans_src << ADDR_OFFSET; +#else + trans_src = jpeg_dev->dc_vram_info.dma_addr; +#endif + if (jpeg_dev->dc_param.width * jpeg_dev->dc_param.height != 0) { + detected_timings->width = jpeg_dev->dc_param.width; + detected_timings->height = jpeg_dev->dc_param.height; + } + + dev_dbg(jpeg_dev->dev, "13 %d %d\n", jpeg_dev->detected_timings.width, + jpeg_dev->active_timings.width); + if (jpeg_dev->detected_timings.width != jpeg_dev->active_timings.width || + jpeg_dev->detected_timings.height != jpeg_dev->active_timings.height) { + phytium_jpeg_off(jpeg_dev); + + v4l2_event_queue(&jpeg_dev->vdev, &event); + set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); + } + + if (jpeg_dev->dc_param.dma_addr < trans_src) + src = jpeg_dev->dc_vram_info.virt_addr; + else + src = jpeg_dev->dc_vram_info.virt_addr + + (jpeg_dev->dc_param.dma_addr - trans_src); + + dst = jpeg_dev->src_addrs[0].virt_addr; + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memcpy(dst, src, jpeg_dev->dc_param.width * 4); + src += jpeg_dev->dc_param.stride; + dst += jpeg_dev->dc_param.width * 4; + } + } else { + dst = jpeg_dev->src_addrs[0].virt_addr; + dev_dbg(jpeg_dev->dev, "21\n"); + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memset(dst, 0, jpeg_dev->dc_param.width * 4); + dst += jpeg_dev->dc_param.width * 4; + } + dev_dbg(jpeg_dev->dev, "22\n"); + } + + while (time_before(jiffies, jpeg_dev->timeout_jiffies)) { + usleep_range(1000, 3000); + }; + + if (!jpeg_dev->frame_rate) { + jpeg_dev->frame_rate = 30; + dev_err(jpeg_dev->dev, "jpeg frame rate is zero\n"); + } + jpeg_dev->timeout_jiffies = jiffies + msecs_to_jiffies(1000/jpeg_dev->frame_rate); + + dev_dbg(jpeg_dev->dev, "23\n"); + if (test_bit(VIDEO_STREAMING, &jpeg_dev->status) && + (!test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status))) { + dev_dbg(jpeg_dev->dev, "14\n"); + phytium_jpeg_reset_init(jpeg_dev); + dev_dbg(jpeg_dev->dev, "17\n"); + phytium_jpeg_start_frame(jpeg_dev); + } + } + + return 0; +} + +static int phytium_jpeg_init(struct phytium_jpeg_dev *jpeg_dev) +{ + int irq; + int ret; + struct device *dev = jpeg_dev->dev; + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) { + dev_err(dev, "Failed to get IRQ\n"); + return -ENODEV; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_irq, + IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ %d\n", irq); + return ret; + } + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret != 0) { + dev_err(dev, "Failed to set DMA mask\n"); + return ret; + } + + phytium_jpeg_get_yuv_mode(jpeg_dev); + + return 0; + +} + +int phytium_jpeg_ddr_mode_init(struct phytium_jpeg_dev *jpeg_dev) +{ + struct device *dev = jpeg_dev->dev; + struct device_node *ddr_mode_node; + unsigned int dc_reg[2], dc_vram[2], jpeg_vram[2], trans_reg[2]; + int ret; + + ddr_mode_node = of_get_child_by_name(jpeg_dev->dev->of_node, "ddr-mode"); + if (!ddr_mode_node) { + dev_err(jpeg_dev->dev, "could not find ddr-mode node\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,dc-reg", + dc_reg, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the dc-reg info from device tree node.\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,dc-vram", + dc_vram, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the dc-vram info from device tree node.\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,jpeg-vram", + jpeg_vram, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the jpeg-vram info from device tree node.\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,trans-reg", + trans_reg, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the trans-reg info from device tree node.\n"); + goto out; + } + +#ifdef BMC_MODE + jpeg_dev->dc_reg = ioremap(dc_reg[0], dc_reg[1]); + if (jpeg_dev->dc_reg == NULL) { + dev_err(dev, "failed to remap dc reg(0x%x)\n", dc_reg[0]); + goto out; + } + + jpeg_dev->trans_reg = ioremap(trans_reg[0], trans_reg[1]); + if (jpeg_dev->trans_reg == NULL) { + dev_err(dev, "failed to remap trans reg(0x%x)\n", trans_reg[0]); + goto failed_remap_trans_reg; + } +#else + unsigned long dc_addr_h = PHYTIUM_DC_ADDR_H; + + jpeg_dev->dc_reg = ((dc_addr_h << 32) | dc_addr); + dev_dbg(jpeg_dev->dev, "dc_addr: 0x%p\n", jpeg_dev->dc_reg); +#endif + + jpeg_dev->dc_vram_info.virt_addr = ioremap_cache(dc_vram[0], dc_vram[1]); + if (jpeg_dev->dc_vram_info.virt_addr == NULL) { + dev_err(dev, "failed to remap dc vram(0x%x)\n", dc_vram[0]); + goto failed_remap_dc_vram; + } + jpeg_dev->dc_vram_info.dma_addr = dc_vram[0]; + jpeg_dev->dc_vram_info.size = dc_vram[1]; + dev_dbg(jpeg_dev->dev, "dc_vram_info.virt_addr:0x%p dma:0x%llx\n", + jpeg_dev->dc_vram_info.virt_addr, jpeg_dev->dc_vram_info.dma_addr); + + jpeg_dev->src_addrs[0].virt_addr = ioremap_cache(jpeg_vram[0], jpeg_vram[1]); + if (jpeg_dev->src_addrs[0].virt_addr == NULL) { + dev_err(dev, "failed to remap jpeg vram(0x%x)\n", jpeg_vram[0]); + goto failed_remap_jpeg_vram; + } + + jpeg_dev->src_addrs[0].dma_addr = jpeg_vram[0]; + jpeg_dev->src_addrs[0].size = jpeg_vram[1]; + dev_dbg(jpeg_dev->dev, "src_addrs[0]:0x%p dma:0x%x\n", jpeg_dev->src_addrs[0].virt_addr, + jpeg_dev->src_addrs[0].dma_addr); + memset(jpeg_dev->src_addrs[0].virt_addr, 0, jpeg_dev->src_addrs[0].size); + + spin_lock_init(&jpeg_dev->reset_lock); + jpeg_dev->kthread = kthread_run(phytium_jpeg_ddr_mode_thread, jpeg_dev, "%s", "jpeg"); + if (IS_ERR(jpeg_dev->kthread)) { + dev_err(dev, "failed to create update vram thread (%ld)\n", + PTR_ERR(jpeg_dev->kthread)); + goto failed_create_kthread; + } + + jpeg_dev->ddr_mode = true; + + return 0; + +failed_create_kthread: + iounmap(jpeg_dev->src_addrs[0].virt_addr); +failed_remap_jpeg_vram: + iounmap(jpeg_dev->dc_vram_info.virt_addr); +failed_remap_dc_vram: + iounmap(jpeg_dev->trans_reg); +failed_remap_trans_reg: + iounmap(jpeg_dev->dc_reg); +out: + jpeg_dev->ddr_mode = false; + + return -1; +} + +void phytium_jpeg_ddr_mode_release(struct phytium_jpeg_dev *jpeg_dev) +{ + if (jpeg_dev->ddr_mode) { + if (jpeg_dev->kthread) + kthread_stop(jpeg_dev->kthread); + + iounmap(jpeg_dev->src_addrs[0].virt_addr); + iounmap(jpeg_dev->dc_vram_info.virt_addr); +#ifdef BMC_MODE + iounmap(jpeg_dev->trans_reg); + iounmap(jpeg_dev->dc_reg); +#endif + jpeg_dev->ddr_mode = false; + } +} + +static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) +{ + struct v4l2_device *v4l2_dev = &jpeg_dev->v4l2_dev; + struct vb2_queue *dst_vq = &jpeg_dev->queue; + struct video_device *vdev = &jpeg_dev->vdev; + int ret; + + jpeg_dev->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; + jpeg_dev->pix_fmt.field = V4L2_FIELD_NONE; + jpeg_dev->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB; /* maybe ARGB */ + jpeg_dev->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE; + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + + ret = v4l2_device_register(jpeg_dev->dev, v4l2_dev); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to register v4l2 device\n"); + return ret; + } + + /* Register how many v4l2 controls to a handler */ + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + dst_vq->dev = v4l2_dev->dev; + dst_vq->lock = &jpeg_dev->video_lock; + dst_vq->ops = &phytium_jpeg_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = jpeg_dev; + dst_vq->buf_struct_size = sizeof(struct phytium_jpeg_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dst_vq->min_buffers_needed = CAPTURE_BUF_NUMBER; + ret = vb2_queue_init(dst_vq); + if (ret) { + dev_err(jpeg_dev->dev, "Failed to init vb2 queue\n"); + goto err_v4l2_register; + } + + vdev->queue = dst_vq; + vdev->fops = &phytium_jpeg_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + vdev->v4l2_dev = v4l2_dev; + strscpy(vdev->name, PHYTIUM_JPEG_NAME, sizeof(vdev->name)); + vdev->vfl_type = VFL_TYPE_GRABBER; /* The newest kernel using VFL_TYPE_VIDEO */ + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = video_device_release_empty; + vdev->ioctl_ops = &phytium_jpeg_ioctl_ops; + vdev->lock = &jpeg_dev->video_lock; + + video_set_drvdata(vdev, jpeg_dev); + ret = video_register_device(vdev, VFL_TYPE_GRABBER, 0); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to register video device\n"); + goto err_video_register; + } + + v4l2_info(v4l2_dev, "phytium JPEG registered as /dev/video%d (%d, %d)\n", + jpeg_dev->vdev.num, VIDEO_MAJOR, jpeg_dev->vdev.minor); + return ret; + +err_video_register: + vb2_queue_release(dst_vq); + +err_v4l2_register: + v4l2_device_unregister(v4l2_dev); + return ret; +} + +static const struct phytium_jpeg_config phytium_jpeg_config = { + .comp_size_read = BUF_LIST_INDEX_CTRL_STS_ADDR(VB_BUF_NO), +}; + +static const struct of_device_id phytium_jpeg_match[] = { + { + .compatible = "phytium,jpeg-vram", + .data = &phytium_jpeg_config, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, phytium_jpeg_match); + +static int phytium_jpeg_probe(struct platform_device *pdev) +{ + struct phytium_jpeg_dev *jpeg_dev; + const struct of_device_id *match; + const struct phytium_jpeg_config *config; + struct resource *res; + int ret; + + jpeg_dev = devm_kzalloc(&pdev->dev, sizeof(*jpeg_dev), GFP_KERNEL); + if (jpeg_dev == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + jpeg_dev->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg_dev->base_addr)) { + dev_err(jpeg_dev->dev, "Failed to ioremap.\n"); + return PTR_ERR(jpeg_dev->base_addr); + } + + match = of_match_node(phytium_jpeg_match, pdev->dev.of_node); + if (match == NULL) { + dev_err(jpeg_dev->dev, "Failed to match.\n"); + return -EINVAL; + } + + config = match->data; + jpeg_dev->comp_size_read = config->comp_size_read; + + jpeg_dev->frame_rate = 30; + jpeg_dev->dev = &pdev->dev; + spin_lock_init(&jpeg_dev->hw_lock); + mutex_init(&jpeg_dev->video_lock); + init_waitqueue_head(&jpeg_dev->wait_update_ddr); + INIT_LIST_HEAD(&jpeg_dev->buffers); + + ret = phytium_jpeg_ddr_mode_init(jpeg_dev); + if (ret < 0) + dev_info(jpeg_dev->dev, "JPEG engine ddr mode init failed\n"); + + ret = phytium_jpeg_init(jpeg_dev); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to initialize the JPEG engine.\n"); + return ret; + } + + ret = phytium_jpeg_setup_video(jpeg_dev); + + return ret; +} + +#define to_phytium_jpeg(x) container_of((x), struct phytium_jpeg_dev, v4l2_dev) +static int phytium_jpeg_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct phytium_jpeg_dev *jpeg_dev = to_phytium_jpeg(v4l2_dev); + + phytium_jpeg_off(jpeg_dev); + + phytium_jpeg_ddr_mode_release(jpeg_dev); + + video_unregister_device(&jpeg_dev->vdev); + + vb2_queue_release(&jpeg_dev->queue); + + v4l2_device_unregister(v4l2_dev); + + of_reserved_mem_device_release(dev); + + return 0; +} + +static struct platform_driver phytium_jpeg_driver = { + .probe = phytium_jpeg_probe, + .remove = phytium_jpeg_remove, + .driver = { + .name = PHYTIUM_JPEG_NAME, + .of_match_table = phytium_jpeg_match, + }, +}; + +module_platform_driver(phytium_jpeg_driver); + +MODULE_DESCRIPTION("Phytium JPEG-VRAM Encoder driver"); +MODULE_AUTHOR("Wang Hao "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/phytium-vram/phytium_vram_core.h b/drivers/media/platform/phytium-vram/phytium_vram_core.h new file mode 100644 index 0000000000..5559c3dccd --- /dev/null +++ b/drivers/media/platform/phytium-vram/phytium_vram_core.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_VRAM_CORE_H +#define _PHYTIUM_VRAM_CORE_H + +#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 +#include +#include +#include + +#define PHYTIUM_JPEG_NAME "phytium-jpeg" +#define MAX_FRAME_RATE 60 +#define MAX_HEIGHT 1080 +#define MAX_WIDTH 1920 +#define MIN_HEIGHT 480 +#define MIN_WIDTH 640 +#define MIN_PIXEL_CLOCK (640 * 480 * 60) /* 640 x 480 x 60Hz */ +#define MAX_PIXEL_CLOCK (1920 * 1080 * 60) /* 1920 x 1080 x 60Hz */ + +#define SOURCE_RESOLUTION_DETECT_TIMEOUT msecs_to_jiffies(500) +#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(0) +#define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) +#define STOP_TIMEOUT msecs_to_jiffies(1000) + +#define INVALID_RESOLUTION_RETRIES 2 +#define CAPTURE_BUF_NUMBER 3 /* using how many buffers */ +#define VB_BUF_NO 0 /* there are 16 buffer, use which one */ + +/* The below macros are defined for the JPEG header of the phytium JPEG Engine */ +#define PHYTIUM_JPEG_HEADER_LEN (256 * 3) +#define PHYTIUM_JPEG_HEADER_SIZE (PHYTIUM_JPEG_HEADER_LEN / sizeof(u32)) +#define PHYTIUM_JPEG_HEADER_H_INDEX 40 +#define PHYTIUM_JPEG_HEADER_W_INDEX 41 + +/* There are two ocm buffers that are used for storaging the inputing video data */ +#define OCM_BUF_NUM 2 + +enum phytium_jpeg_status { + VIDEO_MODE_DETECT_DONE, + VIDEO_RES_CHANGE, + VIDEO_RES_DETECT, + VIDEO_STREAMING, + VIDEO_FRAME_INPRG, + VIDEO_STOPPED, + VIDEO_CLOCKS_ON, + VIDEO_POWEROFF, +}; + +struct phytium_dc_vram_info { + unsigned int size; + dma_addr_t dma_addr; + void *virt_addr; +}; + +struct phytium_jpeg_addr { + unsigned int size; + unsigned int dma_addr; + void *virt_addr; +}; + +struct phytium_jpeg_buffer { + struct vb2_v4l2_buffer vb; + struct list_head link; +}; + +enum jpeg_yuv_mode { + YUV444 = 0x0, + YUV422 = 0x1, + YUV420 = 0x2 +}; + +struct phytium_dc_param { + unsigned int width; + unsigned int height; + unsigned int stride; + unsigned int enable; + unsigned long dma_addr; +}; + +/** + * struct phytium_jpeg - JPEG IP abstraction + * @lock: the mutex protecting this structure + * @hw_lock: spinlock protecting the hw device resource + * @workqueue: decode work queue + * @dev: JPEG device + * @v4l2_dev: v4l2 device for mem2mem mode + * @m2m_dev: v4l2 mem2mem device data + * @alloc_ctx: videobuf2 memory allocator's context + * @dec_vdev: video device node for decoder mem2mem mode + * @dec_reg_base: JPEG registers mapping + * @clk_jdec: JPEG hw working clock + * @clk_jdec_smi: JPEG SMI bus clock + * @larb: SMI device + */ +struct phytium_jpeg_dev { + void __iomem *base_addr; + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_pix_format pix_fmt; + struct v4l2_bt_timings active_timings; + struct v4l2_bt_timings detected_timings; + u32 v4l2_input_status; + struct vb2_queue queue; + struct video_device vdev; + /* v4l2 and videobuf2 lock, protect the structure*/ + struct mutex video_lock; + u32 jpeg_mode; + u32 comp_size_read; + /* buffer list lock, protecting the hw device resource */ + spinlock_t hw_lock; + struct list_head buffers; + unsigned long status; + unsigned int sequence; + unsigned int max_compressed_size; + struct phytium_jpeg_addr src_addrs[OCM_BUF_NUM]; + struct phytium_jpeg_addr dst_addrs[16]; + + bool yuv420; + unsigned int frame_rate; + + void __iomem *dc_reg; + void __iomem *trans_reg; + struct phytium_dc_param dc_param; + struct phytium_dc_vram_info dc_vram_info; + struct task_struct *kthread; + wait_queue_head_t wait_update_ddr; + spinlock_t reset_lock; + bool ddr_mode; + enum jpeg_yuv_mode yuv_mode; + unsigned long timeout_jiffies; +}; + +struct phytium_jpeg_config { + u32 jpeg_mode; + u32 comp_size_read; +}; + +#define YUV_MODE_STR_LEN 8 +#define YUVID 42 + +#endif /* _PHYTIUM_VRAM_CORE_H */ diff --git a/drivers/media/platform/phytium-vram/phytium_vram_reg.h b/drivers/media/platform/phytium-vram/phytium_vram_reg.h new file mode 100644 index 0000000000..cf9a0a58a0 --- /dev/null +++ b/drivers/media/platform/phytium-vram/phytium_vram_reg.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_VRAM_REG_H +#define _PHYTIUM_VRAM_REG_H + +#include +/* define the all kinds of control registers in a JPEG soc */ + +/* The service id call se to turn off ocn jpeg */ +#define PHYTIUM_SET_OCN_JPEG_OFF 0xc300fff4 + +/* The register is used to set the information of the video comes from main memory */ +#define SRC_DDR_INFO_REG 0x00000800 + +#define CFG_V_MASK 0x7ff +#define CFG_V_SHIFT 16 +#define CFG_H_MASK 0xfff +#define CFG_H_SHIFT 4 +#define CFG_IMG BIT(27) +#define CFG_OCM_VALID BIT(28) + + +/* The register is used to get the information of the video comes from external VGA */ +#define SRC_VGA_INFO_REG 0x00000894 + +#define SRC_FORMAT BIT(0) /* 0:RGB888, 1:RGB565 */ +#define SRC_DE_POLARITY BIT(1) /* 0:low level is effect, other */ +#define SRC_HS_POLARITY BIT(2) /* 0:low level is effect, other */ +#define SRC_VS_POLARITY BIT(3) /* 0:low level is effect, other */ +#define SRC_HOR_PIXELS GENMASK(15, 4) /* the number of the horizontal pixels */ +#define SRC_WIDTH_SHIFT 4 /* shift right to get width */ +#define SRC_VER_PIXELS GENMASK(26, 16) /* the number of the vertical pixels */ +#define SRC_HEIGHT_SHIFT 16 /* shift right to get height */ +/* The below bit fields is only used by image comes from main memory */ +#define SRC_COMP_DDR_IMG_EN BIT(27) /* 0: disable to JPEG compression, others */ + +/* marks which ocm buffer is occupied to store an image */ +#define SRC_DDR_IMG2OCM_VALID GENMASK(29, 28) + +/* The register controls starting work of the JPEG engine */ +#define TRANSFORM_INFO_REG 0x00000804 +#define TRANSINFO_ENABLE_ENGINE BIT(0) /* 1: enable the JPEG engine */ +/* 1: video comes from external VGA, 0: video comes from DDR */ +#define TRANSINFO_SRC_SELECT BIT(1) +/* 0: video comes from external VGA is cached to OCM, 1: DDR */ +#define TRANSINFO_IMAGE_STORE BIT(2) +#define TRANSINFO_FRAME_RATE GENMASK(9, 4) /* the value notes frame rate */ +#define TRANSINFO_BLOCK_SIZE BIT(12) /* 0: 8x8, 1: 16x16 */ +#define TRANSINFO_ENABLE_YUV422 BIT(13) /* 1: JPEG block is populated YUV422 */ +/* support burst with the values such as 1, 2, 4, 8, 16, 32, 64. using default value 0xf */ +#define TRANSINFO_AXI_LEN GENMASK(22, 16) +#define TRANS_AXI_LEN_SHIFT 16 + +/* The interrupt and status register */ +#define INT_STATUS_CTRL_REG 0x00000808 +#define INT_FIFO_OVERFLOW BIT(0) /* video fifo overflow, write 1 to clear */ +#define INT_OCM_BUF_OVERFLOW BIT(1) /* ocm buffer overflow, write 1 to clear */ +/* JPEG engine complete compression, write 1 to clear */ +#define INT_JPEG_ENCODE_COMPLETE BIT(2) +/* in VGA mode, video's format is changed */ +#define INT_VIDEO_FORMAT_CHANGE BIT(3) +/* enable the interrupt of th video fifo overflow and source resolution */ +#define DETECT_RESOLUTION_CHANGE_EN BIT(4) +/* enable the interrupt of the ocm buffer overflow */ +#define STS_VE_OCM_BUF_OVERFLOW_EN BIT(5) +/* enable the interrupt of the JPEG complete compression */ +#define STS_VE_JPEG_CODE_COMP_EN BIT(6) +/* in VGA mode, the bit notes ocm buff is busy */ +#define STS_VE_OCM_BUF_BUSY BIT(7) +/* in VGA mode, the bit notes sequence number of the frame */ +#define STS_VE_CUR_FRAME_NUMBER GENMASK(9, 8) +/* in VGA mode, the bit notes sequence number of the cached frame */ +#define STS_VE_BUF_CACHE_NUMBER GENMASK(11, 10) +/* in VGA mode, the buffer number in buffer list */ +#define STS_JPEG_COMP_BUF_NO GENMASK(15, 12) +#define INT_JPEG_COMP_BUF_LIST_NO GENMASK(31, 16) /* the interrupt number of the buffer */ + +#define OCM_BUF0_ADDR 0x0000080C +#define OCM_BUF1_ADDR 0x00000810 +#define OCM_BUF_SHIFT 8 + +#define BUF_LIST_BASE_ADDR 0x00000814 + +#define PHYTIUM_BUF_LIST_ACTRL_AND_STS_BASE_ADDR_REG 0x00000818 +#define STS_JPEG_BUF_HIGH_LEVEL_VALID BIT(0) /* Hight levle is validity */ +#define JPEG_BUF_CAPACITY_SIZE GENMASK(29, 8) /* the capacity of the buffer */ +#define JPEG_BUF_CAPACITY_SIZE_SHIFT 8 + +/* There are 16 buffers in the buffer list, the width between each other' address is 8 bytes */ +#define BUF_LIST_ADDR_OFFSET 0x8 +#define BUF_LIST_CTRL_AND_STS_OFFSET 0x8 + +/* Get the address of the specific index buffer */ +#define BUF_LIST_INDEX_ADDR(index) \ + (BUF_LIST_BASE_ADDR + (index) * BUF_LIST_ADDR_OFFSET) + +#define JPEG_DST_ADDR_SHIFT 8 + +#define BUF_LIST_INDEX_CTRL_STS_ADDR(index) \ + (PHYTIUM_BUF_LIST_ACTRL_AND_STS_BASE_ADDR_REG + (index) * BUF_LIST_CTRL_AND_STS_OFFSET) + +#define FRAME_SAMPLE_CTRL 0x00000898 +#define FRAME_SAMPLE_CTRL_EN BIT(31) +#define FRAME_SAMPLE_INTERVAL GENMASK(30, 0) + +/* The below registers are all related to quantilize */ +#define HUFF_MODE_REG 0x300 +#define SAMPLE_MODE_REG 0x304 + +#define Y_QUANT_BASE_ADDR_REG 0x400 +#define C_QUANT_BASE_ADDR_REG 0x500 + +#define QUANT_REG_NUM 64 + +#define Y_QUANT_INDEX_ADDR_REG(index) \ + (Y_QUANT_BASE_ADDR_REG + 4 * (index)) + +#define C_QUANT_INDEX_ADDR_REG(index) \ + (C_QUANT_BASE_ADDR_REG + 4 * (index)) + +#define PHYTIUM_DC_ADDR_H 0xffffff80UL + +#define PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS 0x1400 +#define PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS 0x1404 +#define PREFIX_MASK 0xff +#define PREFIX_SHIFT 32 + +#define PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE 0x1408 + +#define PHYTIUM_DC_HDISPLAY 0x1430 + #define HDISPLAY_END_SHIFT 0 + #define HDISPLAY_END_MASK 0x7fff + #define HDISPLAY_TOTAL_SHIFT 16 + #define HDISPLAY_TOTAL_MASK 0x7fff + +#define PHYTIUM_DC_VDISPLAY 0x1440 + #define VDISPLAY_END_SHIFT 0 + #define VDISPLAY_END_MASK 0x7fff + #define VDISPLAY_TOTAL_SHIFT 16 + #define VDISPLAY_TOTAL_MASK 0x7fff + +#define PHYTIUM_DC_FRAMEBUFFER_CONFIG 0x1518 + #define FRAMEBUFFER_OUTPUT BIT(0) + #define FRAMEBUFFER_CLEAR BIT(8) + +#define PE220X_DC_ADDR_TRANS_SRC_ADDR 0x0 +#define ADDR_OFFSET 22 +#define ADDR_MASK 0xffffffffff + +#define PE220X_DC_ADDRESS_TRANSFORM_SIZE 0x4 +#define ADDRESS_TRANSFORM_ENABLE (0x1 << 31) +#define SIZE_OFFSET 22 + +#endif /* _PHYTIUM_VRAM_REG_H */ -- Gitee