2024-04-04 05:14:47

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 00/37] Device Tree support for SH7751 based board

This is an updated version of something I wrote about 7 years ago.
Minimum support for R2D-plus and LANDISK.
I think R2D-1 will work if you add AX88796 to dts.
And board-specific functions and SCI's SPI functions are not supported.

You can get it working with qemu found here.
https://gitlab.com/yoshinori.sato/qemu/-/tree/landisk

v7 changes.
- sh/kernel/setup.c: fix kernel parameter handling.
- clk-sh7750.c: cleanup.
- sh_tmu.c: cleanup.
- irq-renesas-sh7751.c: IPR definition move to code.
- irq-renesas-sh7751irl.c: update register definition.
- pci-sh7751.c: Register initialization fix.
- sm501 and sm501fb: Re-design Device Tree properties.

v6 changes.
- pci-sh7751: merge register define.
- pci-sh7751: use 'dma-ranges' property.
- pci-sh7751: rename general PCI properties.
- sm501 and sm501fb: Re-design Device Tree properties.
- sh/kernel/setup: cleanup command line setup.
- irq-sh7751.c: some cleanup.

v5 changes.
- pci-sh7751: revert header changes. and some fix in previuous driver.
- sh/kernel/iomap.c: Use SH io functions.
- sm501 and sm501fb: re-write DT support.

v4 changes.
- cpg-sh7750: use clk-divider and clk-gate.
- pci-sh7751: unified header files to old PCI driver.
- irq-renesas-sh7751: IPR registers direct mapping.
- irq-renesas-sh7751irl: useful register bit mapping.
- sm501 and sm501fb: re-write dt parser.
- j2_minus: fix build error.
- dt-binding schema: fix some errors.
- *.dts: cleanup.

v3 changes.
- Rewrite clk drivers.
- Added sh_tmu to OF support.
- Cleanup PCI stuff.
- Update sm501 and sm501fb OF support.
- Update devicetree and documents.

v2 changes.
- Rebasing v6,6-rc1
- re-write irqchip driver.
- Add binding documents.
- Cleanup review comment.

Yoshinori Sato (37):
sh: passing FDT address to kernel startup.
sh: Kconfig unified OF supported targets.
sh: Enable OF support for build and configuration.
dt-bindings: interrupt-controller: Add header for Renesas SH3/4 INTC.
sh: GENERIC_IRQ_CHIP support for CONFIG_OF=y
sh: kernel/setup Update DT support.
sh: Fix COMMON_CLK support in CONFIG_OF=y.
clocksource: sh_tmu: CLOCKSOURCE support.
dt-binding: Add compatible SH7750 SoC
sh: Common PCI Framework driver support.
pci: pci-sh7751: Add SH7751 PCI driver
dt-bindings: pci: pci-sh7751: Add SH7751 PCI
dt-bindings: clock: sh7750-cpg: Add renesas,sh7750-cpg header.
clk: Compatible with narrow registers
clk: renesas: Add SH7750/7751 CPG Driver
irqchip: Add SH7751 INTC driver
dt-bindings: interrupt-controller: renesas,sh7751-intc: Add
json-schema
irqchip: SH7751 external interrupt encoder with enable gate.
dt-bindings: interrupt-controller: renesas,sh7751-irl-ext: Add
json-schema
serial: sh-sci: fix SH4 OF support.
dt-bindings: serial: renesas,scif: Add scif-sh7751.
dt-bindings: display: smi,sm501: SMI SM501 binding json-schema
dt-bindings: display: sm501 register definition helper
mfd: sm501: Convert platform_data to OF property
dt-binding: sh: cpus: Add SH CPUs json-schema
dt-bindings: vendor-prefixes: Add iodata
dt-bindings: ata: ata-generic: Add new targets
dt-bindings: soc: renesas: sh: Add SH7751 based target
sh: SH7751R SoC Internal peripheral definition dtsi.
sh: add RTS7751R2D Plus DTS
sh: Add IO DATA LANDISK dts
sh: Add IO DATA USL-5P dts
sh: j2_mimas_v2.dts update
sh: Add dtbs target support.
sh: RTS7751R2D Plus OF defconfig
sh: LANDISK OF defconfig
sh: j2_defconfig: update

.../devicetree/bindings/ata/ata-generic.yaml | 2 +
.../bindings/clock/renesas,sh7750-cpg.yaml | 105 ++++
.../bindings/display/smi,sm501.yaml | 398 +++++++++++++++
.../renesas,sh7751-intc.yaml | 53 ++
.../renesas,sh7751-irl-ext.yaml | 57 +++
.../bindings/pci/renesas,sh7751-pci.yaml | 89 ++++
.../bindings/serial/renesas,scif.yaml | 1 +
.../devicetree/bindings/sh/cpus.yaml | 63 +++
.../devicetree/bindings/soc/renesas/sh.yaml | 27 +
.../bindings/timer/renesas,tmu.yaml | 2 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
arch/sh/Kconfig | 33 +-
arch/sh/boards/Kconfig | 23 +-
arch/sh/boards/of-generic.c | 28 +-
arch/sh/boot/compressed/head_32.S | 5 +-
arch/sh/boot/dts/Makefile | 5 +
arch/sh/boot/dts/j2_mimas_v2.dts | 2 +-
arch/sh/boot/dts/landisk.dts | 77 +++
arch/sh/boot/dts/rts7751r2dplus.dts | 169 ++++++
arch/sh/boot/dts/sh7751r.dtsi | 105 ++++
arch/sh/boot/dts/usl-5p.dts | 85 ++++
arch/sh/configs/j2_defconfig | 11 +-
arch/sh/configs/landisk-of_defconfig | 104 ++++
arch/sh/configs/rts7751r2dplus-of_defconfig | 75 +++
arch/sh/drivers/Makefile | 2 +
arch/sh/include/asm/io.h | 8 +
arch/sh/include/asm/irq.h | 10 +-
arch/sh/include/asm/pci.h | 4 +
arch/sh/include/asm/setup.h | 1 +
arch/sh/kernel/cpu/Makefile | 6 +-
arch/sh/kernel/cpu/irq/imask.c | 17 +
arch/sh/kernel/cpu/sh4/Makefile | 3 +
arch/sh/kernel/iomap.c | 18 +
arch/sh/kernel/setup.c | 36 +-
arch/sh/kernel/time.c | 12 +
drivers/clk/clk-divider.c | 56 +-
drivers/clk/clk-gate.c | 62 ++-
drivers/clk/renesas/Kconfig | 13 +-
drivers/clk/renesas/Makefile | 1 +
drivers/clk/renesas/clk-sh7750.c | 480 ++++++++++++++++++
drivers/clocksource/sh_tmu.c | 198 +++++---
drivers/irqchip/Kconfig | 15 +
drivers/irqchip/Makefile | 3 +
drivers/irqchip/irq-renesas-sh7751.c | 282 ++++++++++
drivers/irqchip/irq-renesas-sh7751irl.c | 221 ++++++++
drivers/mfd/sm501.c | 315 ++++++++++++
drivers/pci/controller/Kconfig | 9 +
drivers/pci/controller/Makefile | 1 +
drivers/pci/controller/pci-sh7751.c | 342 +++++++++++++
drivers/tty/serial/Kconfig | 2 +-
drivers/tty/serial/sh-sci.c | 6 +-
drivers/video/fbdev/sm501fb.c | 106 ++++
include/dt-bindings/clock/sh7750-cpg.h | 26 +
include/dt-bindings/display/sm501.h | 76 +++
.../renesas,sh7751-intc.h | 19 +
include/linux/clk-provider.h | 22 +-
include/linux/sh_intc.h | 7 +-
57 files changed, 3713 insertions(+), 187 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.yaml
create mode 100644 Documentation/devicetree/bindings/display/smi,sm501.yaml
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-intc.yaml
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-irl-ext.yaml
create mode 100644 Documentation/devicetree/bindings/pci/renesas,sh7751-pci.yaml
create mode 100644 Documentation/devicetree/bindings/sh/cpus.yaml
create mode 100644 Documentation/devicetree/bindings/soc/renesas/sh.yaml
create mode 100644 arch/sh/boot/dts/landisk.dts
create mode 100644 arch/sh/boot/dts/rts7751r2dplus.dts
create mode 100644 arch/sh/boot/dts/sh7751r.dtsi
create mode 100644 arch/sh/boot/dts/usl-5p.dts
create mode 100644 arch/sh/configs/landisk-of_defconfig
create mode 100644 arch/sh/configs/rts7751r2dplus-of_defconfig
create mode 100644 drivers/clk/renesas/clk-sh7750.c
create mode 100644 drivers/irqchip/irq-renesas-sh7751.c
create mode 100644 drivers/irqchip/irq-renesas-sh7751irl.c
create mode 100644 drivers/pci/controller/pci-sh7751.c
create mode 100644 include/dt-bindings/clock/sh7750-cpg.h
create mode 100644 include/dt-bindings/display/sm501.h
create mode 100644 include/dt-bindings/interrupt-controller/renesas,sh7751-intc.h

--
2.39.2



2024-04-04 05:03:32

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 14/37] clk: Compatible with narrow registers

divider and gate only support 32-bit registers.
Older hardware uses narrower registers, so I want to be able to handle
8-bit and 16-bit wide registers.

Seven clk_divider flags are used, and if I add flags for 8bit access and
16bit access, 8bit will not be enough, so I expanded it to u16.

Signed-off-by: Yoshinori Sato <[email protected]>
---
drivers/clk/clk-divider.c | 56 +++++++++++++++++++++-----------
drivers/clk/clk-gate.c | 62 ++++++++++++++++++++++++++++--------
include/linux/clk-provider.h | 22 ++++++++++---
3 files changed, 103 insertions(+), 37 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index a2c2b5203b0a..a1b5187cd63d 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -26,20 +26,38 @@
* parent - fixed parent. No clk_set_parent support
*/

-static inline u32 clk_div_readl(struct clk_divider *divider)
-{
- if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
- return ioread32be(divider->reg);
-
- return readl(divider->reg);
+static inline u32 clk_div_read(struct clk_divider *divider)
+{
+ if (divider->flags & CLK_DIVIDER_REG_8BIT)
+ return readb(divider->reg);
+ else if (divider->flags & CLK_DIVIDER_REG_16BIT) {
+ if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
+ return ioread16be(divider->reg);
+ else
+ return readw(divider->reg);
+ } else {
+ if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
+ return ioread32be(divider->reg);
+ else
+ return readl(divider->reg);
+ }
}

-static inline void clk_div_writel(struct clk_divider *divider, u32 val)
+static inline void clk_div_write(struct clk_divider *divider, u32 val)
{
- if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
- iowrite32be(val, divider->reg);
- else
- writel(val, divider->reg);
+ if (divider->flags & CLK_DIVIDER_REG_8BIT)
+ writeb(val, divider->reg);
+ else if (divider->flags & CLK_DIVIDER_REG_16BIT) {
+ if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
+ iowrite16be(val, divider->reg);
+ else
+ writew(val, divider->reg);
+ } else {
+ if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
+ iowrite32be(val, divider->reg);
+ else
+ writel(val, divider->reg);
+ }
}

static unsigned int _get_table_maxdiv(const struct clk_div_table *table,
@@ -152,7 +170,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
struct clk_divider *divider = to_clk_divider(hw);
unsigned int val;

- val = clk_div_readl(divider) >> divider->shift;
+ val = clk_div_read(divider) >> divider->shift;
val &= clk_div_mask(divider->width);

return divider_recalc_rate(hw, parent_rate, val, divider->table,
@@ -434,7 +452,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
u32 val;

- val = clk_div_readl(divider) >> divider->shift;
+ val = clk_div_read(divider) >> divider->shift;
val &= clk_div_mask(divider->width);

return divider_ro_round_rate(hw, rate, prate, divider->table,
@@ -455,7 +473,7 @@ static int clk_divider_determine_rate(struct clk_hw *hw,
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
u32 val;

- val = clk_div_readl(divider) >> divider->shift;
+ val = clk_div_read(divider) >> divider->shift;
val &= clk_div_mask(divider->width);

return divider_ro_determine_rate(hw, req, divider->table,
@@ -505,11 +523,11 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
val = clk_div_mask(divider->width) << (divider->shift + 16);
} else {
- val = clk_div_readl(divider);
+ val = clk_div_read(divider);
val &= ~(clk_div_mask(divider->width) << divider->shift);
}
val |= (u32)value << divider->shift;
- clk_div_writel(divider, val);
+ clk_div_write(divider, val);

if (divider->lock)
spin_unlock_irqrestore(divider->lock, flags);
@@ -538,7 +556,7 @@ struct clk_hw *__clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u32 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_divider *div;
@@ -610,7 +628,7 @@ EXPORT_SYMBOL_GPL(__clk_hw_register_divider);
struct clk *clk_register_divider_table(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
+ u32 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock)
{
struct clk_hw *hw;
@@ -664,7 +682,7 @@ struct clk_hw *__devm_clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u32 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock)
{
struct clk_hw **ptr, *hw;
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 68e585a02fd9..65191f9684ff 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -24,20 +24,38 @@
* parent - fixed parent. No clk_set_parent support
*/

-static inline u32 clk_gate_readl(struct clk_gate *gate)
+static inline u32 clk_gate_read(struct clk_gate *gate)
{
- if (gate->flags & CLK_GATE_BIG_ENDIAN)
- return ioread32be(gate->reg);
-
- return readl(gate->reg);
+ if (gate->flags & CLK_GATE_REG_8BIT)
+ return readb(gate->reg);
+ else if (gate->flags & CLK_GATE_REG_16BIT) {
+ if (gate->flags & CLK_GATE_BIG_ENDIAN)
+ return ioread16be(gate->reg);
+ else
+ return readw(gate->reg);
+ } else {
+ if (gate->flags & CLK_GATE_BIG_ENDIAN)
+ return ioread32be(gate->reg);
+ else
+ return readl(gate->reg);
+ }
}

-static inline void clk_gate_writel(struct clk_gate *gate, u32 val)
+static inline void clk_gate_write(struct clk_gate *gate, u32 val)
{
- if (gate->flags & CLK_GATE_BIG_ENDIAN)
- iowrite32be(val, gate->reg);
- else
- writel(val, gate->reg);
+ if (gate->flags & CLK_GATE_REG_8BIT)
+ writeb(val, gate->reg);
+ else if (gate->flags & CLK_GATE_REG_16BIT) {
+ if (gate->flags & CLK_GATE_BIG_ENDIAN)
+ iowrite16be(val, gate->reg);
+ else
+ writew(val, gate->reg);
+ } else {
+ if (gate->flags & CLK_GATE_BIG_ENDIAN)
+ iowrite32be(val, gate->reg);
+ else
+ writel(val, gate->reg);
+ }
}

/*
@@ -72,7 +90,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
if (set)
reg |= BIT(gate->bit_idx);
} else {
- reg = clk_gate_readl(gate);
+ reg = clk_gate_read(gate);

if (set)
reg |= BIT(gate->bit_idx);
@@ -80,7 +98,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
reg &= ~BIT(gate->bit_idx);
}

- clk_gate_writel(gate, reg);
+ clk_gate_write(gate, reg);

if (gate->lock)
spin_unlock_irqrestore(gate->lock, flags);
@@ -105,7 +123,7 @@ int clk_gate_is_enabled(struct clk_hw *hw)
u32 reg;
struct clk_gate *gate = to_clk_gate(hw);

- reg = clk_gate_readl(gate);
+ reg = clk_gate_read(gate);

/* if a set bit disables this clk, flip it before masking */
if (gate->flags & CLK_GATE_SET_TO_DISABLE)
@@ -137,12 +155,30 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
struct clk_init_data init = {};
int ret = -EINVAL;

+ /* validate register size option and bit_idx */
if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
if (bit_idx > 15) {
pr_err("gate bit exceeds LOWORD field\n");
return ERR_PTR(-EINVAL);
}
}
+ if (clk_gate_flags & CLK_GATE_REG_16BIT) {
+ if (bit_idx > 15) {
+ pr_err("gate bit exceeds 16 bits\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+ if (clk_gate_flags & CLK_GATE_REG_8BIT) {
+ if (bit_idx > 7) {
+ pr_err("gate bit exceeds 8 bits\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+ if ((clk_gate_flags & CLK_GATE_HIWORD_MASK) &&
+ clk_gate_flags & (CLK_GATE_REG_8BIT | CLK_GATE_REG_16BIT)) {
+ pr_err("HIWORD_MASK required 32-bit register\n");
+ return ERR_PTR(-EINVAL);
+ }

/* allocate the gate */
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 4a537260f655..eaa6ff1d0b2e 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -508,12 +508,16 @@ void of_fixed_clk_setup(struct device_node *np);
* CLK_GATE_BIG_ENDIAN - by default little endian register accesses are used for
* the gate register. Setting this flag makes the register accesses big
* endian.
+ * CLK_GATE_REG_8BIT - by default 32bit register accesses are used for
+ * the gate register. Setting this flag makes the register accesses 8bit.
+ * CLK_GATE_REG_16BIT - by default 32bit register accesses are used for
+ * the gate register. Setting this flag makes the register accesses 16bit.
*/
struct clk_gate {
struct clk_hw hw;
void __iomem *reg;
u8 bit_idx;
- u8 flags;
+ u32 flags;
spinlock_t *lock;
};

@@ -522,6 +526,8 @@ struct clk_gate {
#define CLK_GATE_SET_TO_DISABLE BIT(0)
#define CLK_GATE_HIWORD_MASK BIT(1)
#define CLK_GATE_BIG_ENDIAN BIT(2)
+#define CLK_GATE_REG_8BIT BIT(3)
+#define CLK_GATE_REG_16BIT BIT(4)

extern const struct clk_ops clk_gate_ops;
struct clk_hw *__clk_hw_register_gate(struct device *dev,
@@ -675,13 +681,17 @@ struct clk_div_table {
* CLK_DIVIDER_BIG_ENDIAN - By default little endian register accesses are used
* for the divider register. Setting this flag makes the register accesses
* big endian.
+ * CLK_DIVIDER_REG_8BIT - by default 32bit register accesses are used for
+ * the gate register. Setting this flag makes the register accesses 8bit.
+ * CLK_DIVIDER_REG_16BIT - by default 32bit register accesses are used for
+ * the gate register. Setting this flag makes the register accesses 16bit.
*/
struct clk_divider {
struct clk_hw hw;
void __iomem *reg;
u8 shift;
u8 width;
- u8 flags;
+ u16 flags;
const struct clk_div_table *table;
spinlock_t *lock;
};
@@ -697,6 +707,8 @@ struct clk_divider {
#define CLK_DIVIDER_READ_ONLY BIT(5)
#define CLK_DIVIDER_MAX_AT_ZERO BIT(6)
#define CLK_DIVIDER_BIG_ENDIAN BIT(7)
+#define CLK_DIVIDER_REG_8BIT BIT(8)
+#define CLK_DIVIDER_REG_16BIT BIT(9)

extern const struct clk_ops clk_divider_ops;
extern const struct clk_ops clk_divider_ro_ops;
@@ -726,18 +738,18 @@ struct clk_hw *__clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u32 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock);
struct clk_hw *__devm_clk_hw_register_divider(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data, unsigned long flags,
- void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags,
+ void __iomem *reg, u8 shift, u8 width, u32 clk_divider_flags,
const struct clk_div_table *table, spinlock_t *lock);
struct clk *clk_register_divider_table(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, const struct clk_div_table *table,
+ u32 clk_divider_flags, const struct clk_div_table *table,
spinlock_t *lock);
/**
* clk_register_divider - register a divider clock with the clock framework
--
2.39.2


2024-04-04 05:03:30

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 13/37] dt-bindings: clock: sh7750-cpg: Add renesas,sh7750-cpg header.

SH7750 CPG Clock output define.

Signed-off-by: Yoshinori Sato <[email protected]>
---
.../bindings/clock/renesas,sh7750-cpg.yaml | 105 ++++++++++++++++++
include/dt-bindings/clock/sh7750-cpg.h | 26 +++++
2 files changed, 131 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.yaml
create mode 100644 include/dt-bindings/clock/sh7750-cpg.h

diff --git a/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.yaml b/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.yaml
new file mode 100644
index 000000000000..04c10b0834ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/renesas,sh7750-cpg.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/renesas,sh7750-cpg.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas SH7750/7751 Clock Pulse Generator (CPG)
+
+maintainers:
+ - Yoshinori Sato <[email protected]>
+
+description:
+ The Clock Pulse Generator (CPG) generates core clocks for the SoC. It
+ includes PLLs, and variable ratio dividers.
+
+ The CPG may also provide a Clock Domain for SoC devices, in combination with
+ the CPG Module Stop (MSTP) Clocks.
+
+properties:
+ compatible:
+ enum:
+ - renesas,sh7750-cpg # SH7750
+ - renesas,sh7750s-cpg # SH775S
+ - renesas,sh7750r-cpg # SH7750R
+ - renesas,sh7751-cpg # SH7751
+ - renesas,sh7751r-cpg # SH7751R
+
+ reg: true
+
+ reg-names: true
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: extal
+
+ '#clock-cells':
+ const: 1
+
+ renesas,mode:
+ description: Board-specific settings of the MD[0-2] pins on SoC
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 6
+
+ '#power-domain-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - '#clock-cells'
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - renesas,sh7750-cpg
+ - renesas,sh7750s-cpg
+ then:
+ properties:
+ reg:
+ maxItems: 1
+ reg-names:
+ items:
+ - const: FRQCR
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - renesas,sh7750r-cpg
+ - renesas,sh7751-cpg
+ - renesas,sh7751r-cpg
+ then:
+ properties:
+ reg:
+ maxItems: 2
+ reg-names:
+ items:
+ - const: FRQCR
+ - const: CLKSTP00
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/sh7750-cpg.h>
+ cpg: clock-controller@ffc00000 {
+ #clock-cells = <1>;
+ #power-domain-cells = <0>;
+ compatible = "renesas,sh7751r-cpg";
+ clocks = <&extal>;
+ clock-names = "extal";
+ reg = <0xffc00000 20>, <0xfe0a0000 16>;
+ reg-names = "FRQCR", "CLKSTP00";
+ renesas,mode = <0>;
+ };
diff --git a/include/dt-bindings/clock/sh7750-cpg.h b/include/dt-bindings/clock/sh7750-cpg.h
new file mode 100644
index 000000000000..ec267be91adf
--- /dev/null
+++ b/include/dt-bindings/clock/sh7750-cpg.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+ *
+ * Copyright 2023 Yoshinori Sato
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_SH7750_H__
+#define __DT_BINDINGS_CLOCK_SH7750_H__
+
+#define SH7750_CPG_PLLOUT 0
+
+#define SH7750_CPG_PCK 1
+#define SH7750_CPG_BCK 2
+#define SH7750_CPG_ICK 3
+
+#define SH7750_MSTP_SCI 4
+#define SH7750_MSTP_RTC 5
+#define SH7750_MSTP_TMU012 6
+#define SH7750_MSTP_SCIF 7
+#define SH7750_MSTP_DMAC 8
+#define SH7750_MSTP_UBC 9
+#define SH7750_MSTP_SQ 10
+#define SH7750_CSTP_INTC 11
+#define SH7750_CSTP_TMU34 12
+#define SH7750_CSTP_PCIC 13
+
+#endif
--
2.39.2


2024-04-04 05:03:03

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 12/37] dt-bindings: pci: pci-sh7751: Add SH7751 PCI

Renesas SH7751 PCI Controller json-schema.

Signed-off-by: Yoshinori Sato <[email protected]>
---
.../bindings/pci/renesas,sh7751-pci.yaml | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/renesas,sh7751-pci.yaml

diff --git a/Documentation/devicetree/bindings/pci/renesas,sh7751-pci.yaml b/Documentation/devicetree/bindings/pci/renesas,sh7751-pci.yaml
new file mode 100644
index 000000000000..115c2bb67339
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/renesas,sh7751-pci.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/renesas,sh7751-pci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas SH7751 PCI Host controller
+
+maintainers:
+ - Yoshinori Sato <[email protected]>
+
+allOf:
+ - $ref: /schemas/pci/pci-bus.yaml#
+
+properties:
+ compatible:
+ const: renesas,sh7751-pci
+
+ reg:
+ minItems: 2
+ maxItems: 2
+
+ reg-names:
+ items:
+ - const: PCI Controller
+ - const: Bus State Controller
+
+ "#interrupt-cells":
+ const: 1
+
+ "#address-cells":
+ const: 3
+
+ "#size-cells":
+ const: 2
+
+ ranges: true
+
+ dma-ranges: true
+
+ interrupt-controller: true
+
+ renesas,bus-arbit-round-robin:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: |
+ Set DMA bus arbitration to round robin.
+
+required:
+ - compatible
+ - reg
+ - "#interrupt-cells"
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+ - interrupt-map
+ - interrupt-map-mask
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ pci@fe200000 {
+ compatible = "renesas,sh7751-pci";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ device_type = "pci";
+ bus-range = <0 0>;
+ ranges = <0x02000000 0 0xfd000000 0xfd000000 0 0x01000000>,
+ <0x01000000 0 0x00000000 0xfe240000 0 0x00040000>;
+ dma-ranges = <0x02000000 0 0xc000000 0x0c000000 0 0x04000000>;
+ reg = <0xfe200000 0x0400>,
+ <0xff800000 0x0100>;
+ interrupt-map = <0x0000 0 0 1 &julianintc 5 IRQ_TYPE_LEVEL_LOW>,
+ <0x0000 0 0 2 &julianintc 6 IRQ_TYPE_LEVEL_LOW>,
+ <0x0000 0 0 3 &julianintc 7 IRQ_TYPE_LEVEL_LOW>,
+ <0x0000 0 0 4 &julianintc 8 IRQ_TYPE_LEVEL_LOW>,
+ <0x0800 0 0 1 &julianintc 6 IRQ_TYPE_LEVEL_LOW>,
+ <0x0800 0 0 2 &julianintc 7 IRQ_TYPE_LEVEL_LOW>,
+ <0x0800 0 0 3 &julianintc 8 IRQ_TYPE_LEVEL_LOW>,
+ <0x0800 0 0 4 &julianintc 5 IRQ_TYPE_LEVEL_LOW>,
+ <0x1000 0 0 1 &julianintc 7 IRQ_TYPE_LEVEL_LOW>,
+ <0x1000 0 0 2 &julianintc 8 IRQ_TYPE_LEVEL_LOW>,
+ <0x1000 0 0 3 &julianintc 5 IRQ_TYPE_LEVEL_LOW>,
+ <0x1000 0 0 4 &julianintc 6 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-map-mask = <0x1800 0 0 7>;
+ };
--
2.39.2


2024-04-04 05:05:16

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 17/37] dt-bindings: interrupt-controller: renesas,sh7751-intc: Add json-schema

Renesas SH7751 INTC json-schema.

Signed-off-by: Yoshinori Sato <[email protected]>
---
.../renesas,sh7751-intc.yaml | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-intc.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-intc.yaml
new file mode 100644
index 000000000000..fb924eff465d
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-intc.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/renesas,sh7751-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas SH7751 Interrupt Controller
+
+maintainers:
+ - Yoshinori Sato <[email protected]>
+
+properties:
+ compatible:
+ items:
+ - const: renesas,sh7751-intc
+
+ '#interrupt-cells':
+ const: 1
+
+ interrupt-controller: true
+
+ reg:
+ maxItems: 2
+
+ reg-names:
+ items:
+ - const: ICR
+ - const: INTPRI00
+
+ renesas,icr-irlm:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: If true four independent interrupt requests mode (ICR.IRLM is 1).
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - '#interrupt-cells'
+ - interrupt-controller
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/renesas,sh7751-intc.h>
+ shintc: interrupt-controller@ffd00000 {
+ compatible = "renesas,sh7751-intc";
+ reg = <0xffd00000 14>, <0xfe080000 128>;
+ reg-names = "ICR", "INTPRI00";
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ };
+...
--
2.39.2


2024-04-04 05:05:02

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 15/37] clk: renesas: Add SH7750/7751 CPG Driver

Renesas SH7750 and SH7751 series CPG driver.
This driver supported frequency control and clock gating.

Signed-off-by: Yoshinori Sato <[email protected]>
---
drivers/clk/renesas/Kconfig | 13 +-
drivers/clk/renesas/Makefile | 1 +
drivers/clk/renesas/clk-sh7750.c | 480 +++++++++++++++++++++++++++++++
3 files changed, 491 insertions(+), 3 deletions(-)
create mode 100644 drivers/clk/renesas/clk-sh7750.c

diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig
index d252150402e8..482efcb6e76e 100644
--- a/drivers/clk/renesas/Kconfig
+++ b/drivers/clk/renesas/Kconfig
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0

config CLK_RENESAS
- bool "Renesas SoC clock support" if COMPILE_TEST && !ARCH_RENESAS
- default y if ARCH_RENESAS
+ bool "Renesas SoC clock support" if COMPILE_TEST && !ARCH_RENESAS && !SUPERH
+ default y if ARCH_RENESAS || SUPERH
select CLK_EMEV2 if ARCH_EMEV2
select CLK_RZA1 if ARCH_R7S72100
select CLK_R7S9210 if ARCH_R7S9210
@@ -41,6 +41,9 @@ config CLK_RENESAS
select CLK_R9A08G045 if ARCH_R9A08G045
select CLK_R9A09G011 if ARCH_R9A09G011
select CLK_SH73A0 if ARCH_SH73A0
+ select CLK_SH7750 if CPU_SUBTYPE_SH7750 || CPU_SUBTYPE_SH7750S || \
+ CPU_SUBTYPE_SH7750R || CPU_SUBTYPE_SH7751 || \
+ CPU_SUBTYPE_SH7751R

if CLK_RENESAS

@@ -198,7 +201,6 @@ config CLK_SH73A0
select CLK_RENESAS_CPG_MSTP
select CLK_RENESAS_DIV6

-
# Family
config CLK_RCAR_CPG_LIB
bool "CPG/MSSR library functions" if COMPILE_TEST
@@ -228,6 +230,11 @@ config CLK_RZG2L
bool "Renesas RZ/{G2L,G2UL,G3S,V2L} family clock support" if COMPILE_TEST
select RESET_CONTROLLER

+config CLK_SH7750
+ bool "Renesas SH7750/7751 family clock support" if COMPILE_TEST
+ help
+ This is a driver for SH7750 / SH7751 CPG.
+
# Generic
config CLK_RENESAS_CPG_MSSR
bool "CPG/MSSR clock support" if COMPILE_TEST
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile
index f7e18679c3b8..ea0ffa8d59c4 100644
--- a/drivers/clk/renesas/Makefile
+++ b/drivers/clk/renesas/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_CLK_R9A07G054) += r9a07g044-cpg.o
obj-$(CONFIG_CLK_R9A08G045) += r9a08g045-cpg.o
obj-$(CONFIG_CLK_R9A09G011) += r9a09g011-cpg.o
obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o
+obj-$(CONFIG_CLK_SH7750) += clk-sh7750.o

# Family
obj-$(CONFIG_CLK_RCAR_CPG_LIB) += rcar-cpg-lib.o
diff --git a/drivers/clk/renesas/clk-sh7750.c b/drivers/clk/renesas/clk-sh7750.c
new file mode 100644
index 000000000000..043269d31200
--- /dev/null
+++ b/drivers/clk/renesas/clk-sh7750.c
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas SH7750/51 CPG driver
+ *
+ * Copyright 2023 Yoshinori Sato <[email protected]>
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+/* PCLK divide rate selector */
+static const struct clk_div_table pdiv_table[] = {
+ { .val = 0, .div = 2, },
+ { .val = 1, .div = 3, },
+ { .val = 2, .div = 4, },
+ { .val = 3, .div = 6, },
+ { .val = 4, .div = 8, },
+ { }
+};
+
+/* ICLK and BCLK divide rate selector */
+static const struct clk_div_table div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 3, },
+ { .val = 3, .div = 4, },
+ { .val = 4, .div = 6, },
+ { .val = 5, .div = 8, },
+ { }
+};
+
+struct cpg_priv {
+ struct clk_hw hw;
+ spinlock_t clklock;
+ void __iomem *frqcr;
+ void __iomem *clkstp00;
+ u32 mode;
+ u32 feat;
+};
+
+/* CPG feature flag */
+#define CPG_DIV1 BIT(0) /* 7750, 7750S, 7751 */
+#define MSTP_CR2 BIT(1) /* 7750S, 7750R, 7751, 7751R */
+#define MSTP_CLKSTP BIT(2) /* 7750R, 7751, 7751R */
+#define MSTP_CSTP2 BIT(3) /* 7751, 7751R */
+
+enum {
+ CPG_SH7750,
+ CPG_SH7750S,
+ CPG_SH7750R,
+ CPG_SH7751,
+ CPG_SH7751R,
+};
+
+static const u32 cpg_feature[] = {
+ [CPG_SH7750] = CPG_DIV1,
+ [CPG_SH7750S] = CPG_DIV1 | MSTP_CR2,
+ [CPG_SH7750R] = MSTP_CR2 | MSTP_CLKSTP,
+ [CPG_SH7751] = CPG_DIV1 | MSTP_CR2 | MSTP_CLKSTP | MSTP_CSTP2,
+ [CPG_SH7751R] = MSTP_CR2 | MSTP_CLKSTP | MSTP_CSTP2,
+};
+
+enum clk_type {CLK_DIV, CLK_STBCR, CLK_STBCR2, CLK_CLKSTP00};
+
+enum {
+ FRQCR = 0,
+ STBCR = 4,
+ WTCNT = 8,
+ WTCSR = 12,
+ STBCR2 = 16,
+ CLKSTP00 = 0,
+ CLKSTPCLR00 = 8,
+};
+
+static struct cpg_priv *cpg_data;
+
+#define to_priv(_hw) container_of(_hw, struct cpg_priv, hw)
+
+#define FRQCR_PLL1EN BIT(10)
+static const unsigned int pll1mult[] = { 12, 12, 6, 12, 6, 12, 1};
+
+static unsigned long pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cpg_priv *cpg = to_priv(hw);
+ unsigned long rate = parent_rate;
+ u16 frqcr;
+
+ frqcr = ioread16(cpg->frqcr);
+ if (frqcr & FRQCR_PLL1EN) {
+ rate *= pll1mult[cpg->mode];
+ if (cpg->mode < 6 && (cpg->feat & CPG_DIV1))
+ rate /= 2;
+ }
+ return rate;
+}
+
+static void get_round_rate(struct cpg_priv *cpg,
+ unsigned long *out, bool *pllen,
+ unsigned long rate, unsigned long prate)
+{
+ long pllout, res;
+ bool pll;
+
+ if (cpg->mode < 6 && (cpg->feat & CPG_DIV1))
+ prate /= 2;
+
+ pllout = prate * pll1mult[cpg->mode];
+ if (abs(pllout - rate) > abs(prate - rate)) {
+ res = prate;
+ pll = false;
+ } else {
+ res = pllout;
+ pll = true;
+ }
+ if (out)
+ *out = res;
+ if (pllen)
+ *pllen = pll;
+}
+
+static int pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct cpg_priv *cpg = to_priv(hw);
+
+ get_round_rate(cpg, &req->rate, NULL, req->rate, req->best_parent_rate);
+ return 0;
+}
+
+static int pll_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long prate)
+{
+ struct cpg_priv *cpg = to_priv(hw);
+ bool oldpll, newpll;
+ u16 frqcr;
+
+ frqcr = ioread16(cpg->frqcr);
+ get_round_rate(cpg, NULL, &newpll, rate, prate);
+ oldpll = frqcr & FRQCR_PLL1EN;
+ frqcr &= ~FRQCR_PLL1EN;
+
+ if (newpll) {
+ frqcr |= FRQCR_PLL1EN;
+ if (!oldpll) {
+ /* set PLL wakeup delay time */
+ iowrite16(0xa500, cpg->frqcr + WTCNT);
+ iowrite16(0xa507, cpg->frqcr + WTCNT);
+ iowrite16(0x5a00, cpg->frqcr + WTCSR);
+ }
+ }
+ iowrite16(frqcr, cpg->frqcr);
+
+ /* Test for new PLL state */
+ frqcr = ioread16(cpg->frqcr);
+ oldpll = frqcr & FRQCR_PLL1EN;
+ return !(oldpll == newpll);
+}
+
+static const struct clk_ops pll_ops = {
+ .recalc_rate = pll_recalc_rate,
+ .determine_rate = pll_determine_rate,
+ .set_rate = pll_set_rate,
+};
+
+#define PLLOUT "pllout"
+
+static int register_pll(struct device_node *node, struct cpg_priv *cpg)
+{
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init = {
+ .name = PLLOUT,
+ .ops = &pll_ops,
+ .flags = 0,
+ .num_parents = 1,
+ };
+ int ret;
+
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ cpg->hw.init = &init;
+
+ ret = of_clk_hw_register(node, &cpg->hw);
+ if (ret < 0)
+ pr_err("%pOF: failed to add provider %s (%d)\n",
+ node, clk_name, ret);
+ return ret;
+}
+
+static void clkstp00_sw(struct clk_hw *hw, bool on)
+{
+ u32 val;
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ val = BIT(gate->bit_idx);
+ if (on)
+ writel(val, gate->reg + CLKSTPCLR00);
+ else
+ writel(val, gate->reg);
+}
+
+static int clkstp00_enable(struct clk_hw *hw)
+{
+ clkstp00_sw(hw, true);
+ return 0;
+}
+
+static void clkstp00_disable(struct clk_hw *hw)
+{
+ clkstp00_sw(hw, false);
+}
+
+static int clkstp00_is_enabled(struct clk_hw *hw)
+{
+ u8 val;
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ val = readb(gate->reg);
+ val &= 1 << gate->bit_idx;
+ return val == 0;
+}
+
+static const struct clk_ops gate_clkstp00_ops = {
+ .enable = clkstp00_enable,
+ .disable = clkstp00_disable,
+ .is_enabled = clkstp00_is_enabled,
+};
+
+static struct clk_hw *clk_hw_register_clkstp(struct device_node *node,
+ const char *name,
+ const char *parent,
+ void __iomem *reg, int bit,
+ spinlock_t *lock)
+{
+ struct clk_gate *gate;
+ struct clk_init_data init = {
+ .name = name,
+ .ops = &gate_clkstp00_ops,
+ .flags = 0,
+ .parent_names = &parent,
+ .num_parents = 1,
+ };
+ struct clk_hw *hw;
+ int ret;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (gate == NULL)
+ goto error;
+ gate->reg = reg;
+ gate->bit_idx = bit;
+ gate->flags = 0;
+ gate->lock = lock;
+ gate->hw.init = &init;
+ hw = &gate->hw;
+ ret = of_clk_hw_register(node, hw);
+ if (ret < 0)
+ goto error;
+ return hw;
+error:
+ kfree(gate);
+ return ERR_PTR(ret);
+}
+
+#define STBCR_BASE 5
+#define CLKSTP_BASE 2
+static int register_div(struct device_node *node, struct cpg_priv *cpg)
+{
+ static const char * const divout[] = {
+ "fck", "bck", "ick",
+ };
+ static const char * const stbcrout[] = {
+ "sci_clk", "rtc_clk", "tmu012_clk", /* STBCR */
+ "scif_clk", "dmac_clk", /* STBCR */
+ "ubc_clk", "sq_clk", /* STBCR2 */
+ };
+ static const char * const clkstpout[] = {
+ "intc_clk", "tmu34_clk", "pcic_clk", /* CLKSTP00 */
+ };
+
+ int num_clk = ARRAY_SIZE(divout) + ARRAY_SIZE(stbcrout) + ARRAY_SIZE(clkstpout);
+ struct clk_hw_onecell_data *data;
+ struct clk_hw *reg_hw;
+ unsigned int i, n;
+ int ret;
+
+ data = kzalloc(struct_size(data, hws, num_clk + 1), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ num_clk = 0;
+ for (i = 0; i < ARRAY_SIZE(divout); i++) {
+ reg_hw = __clk_hw_register_divider(NULL, node, divout[i],
+ PLLOUT, NULL, NULL,
+ 0, cpg->frqcr, i * 3, 3,
+ CLK_DIVIDER_REG_16BIT,
+ (i == 0) ? pdiv_table : div_table,
+ &cpg->clklock);
+ if (IS_ERR(reg_hw)) {
+ ret = PTR_ERR(reg_hw);
+ goto error;
+ }
+ data->hws[num_clk++] = reg_hw;
+ }
+
+ n = (cpg->feat & MSTP_CR2) ? ARRAY_SIZE(stbcrout) : STBCR_BASE;
+ for (i = 0; i < n; i++) {
+ u32 off = (i < (ARRAY_SIZE(stbcrout) - 2)) ? STBCR : STBCR2;
+
+ reg_hw = __clk_hw_register_gate(NULL, node, stbcrout[i],
+ divout[0], NULL, NULL,
+ 0, cpg->frqcr + off, i % STBCR_BASE,
+ CLK_GATE_REG_8BIT | CLK_GATE_SET_TO_DISABLE,
+ &cpg->clklock);
+ if (IS_ERR(reg_hw)) {
+ ret = PTR_ERR(reg_hw);
+ goto error;
+ }
+ data->hws[num_clk++] = reg_hw;
+ }
+
+ if (cpg->feat & MSTP_CLKSTP) {
+ n = (cpg->feat & MSTP_CSTP2) ? ARRAY_SIZE(clkstpout) : CLKSTP_BASE;
+ for (i = 0; i < n; i++) {
+ reg_hw = clk_hw_register_clkstp(node, clkstpout[i],
+ divout[0], cpg->clkstp00,
+ i, &cpg->clklock);
+ if (IS_ERR(reg_hw)) {
+ ret = PTR_ERR(reg_hw);
+ goto error;
+ }
+ data->hws[num_clk++] = reg_hw;
+ }
+ }
+
+ data->num = num_clk;
+ ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data);
+ if (ret < 0)
+ goto error;
+ return 0;
+
+error:
+ pr_err("%pOF: failed to register clock (%d)\n",
+ node, ret);
+ for (num_clk--; num_clk >= 0; num_clk--)
+ kfree(data->hws[num_clk]);
+ kfree(data);
+ return ret;
+}
+
+#define NR_CLKMODE 7
+static int sh7750_cpg_setup(struct device_node *node, u32 feat)
+{
+ struct cpg_priv *cpg;
+ u32 mode = NR_CLKMODE;
+ int ret = 0;
+
+ cpg_data = NULL;
+
+ of_property_read_u32_index(node, "renesas,mode", 0, &mode);
+ if (mode >= NR_CLKMODE) {
+ pr_err("%s: Invalid clock mode setting (%u)\n",
+ node->name, mode);
+ return -EINVAL;
+ }
+
+ cpg = kzalloc(sizeof(struct cpg_priv), GFP_KERNEL);
+ if (!cpg)
+ return -ENOMEM;
+
+ cpg->frqcr = of_iomap(node, 0);
+ if (cpg->frqcr == NULL) {
+ pr_err("%pOF: failed to map divide register", node);
+ ret = -ENODEV;
+ goto cpg_free;
+ }
+
+ if (feat & MSTP_CLKSTP) {
+ cpg->clkstp00 = of_iomap(node, 1);
+ if (cpg->clkstp00 == NULL) {
+ pr_err("%pOF: failed to map clkstp00 register", node);
+ ret = -ENODEV;
+ goto unmap_frqcr;
+ }
+ }
+ cpg->feat = feat;
+ cpg->mode = mode;
+
+ ret = register_pll(node, cpg);
+ if (ret < 0)
+ goto unmap_clkstp00;
+
+ ret = register_div(node, cpg);
+ if (ret < 0)
+ goto unmap_clkstp00;
+
+ cpg_data = cpg;
+ return 0;
+
+unmap_clkstp00:
+ iounmap(cpg->clkstp00);
+unmap_frqcr:
+ iounmap(cpg->frqcr);
+cpg_free:
+ kfree(cpg);
+ return ret;
+}
+
+static void __init sh7750_cpg_init(struct device_node *node)
+{
+ sh7750_cpg_setup(node, cpg_feature[CPG_SH7750]);
+}
+
+static void __init sh7750s_cpg_init(struct device_node *node)
+{
+ sh7750_cpg_setup(node, cpg_feature[CPG_SH7750S]);
+}
+
+static void __init sh7750r_cpg_init(struct device_node *node)
+{
+ sh7750_cpg_setup(node, cpg_feature[CPG_SH7750R]);
+}
+
+static void __init sh7751_cpg_init(struct device_node *node)
+{
+ sh7750_cpg_setup(node, cpg_feature[CPG_SH7751]);
+}
+
+static void __init sh7751r_cpg_init(struct device_node *node)
+{
+ sh7750_cpg_setup(node, cpg_feature[CPG_SH7751R]);
+}
+
+CLK_OF_DECLARE_DRIVER(sh7750_cpg, "renesas,sh7750-cpg",
+ sh7750_cpg_init);
+CLK_OF_DECLARE_DRIVER(sh7750s_cpg, "renesas,sh7750s-cpg",
+ sh7750s_cpg_init);
+CLK_OF_DECLARE_DRIVER(sh7750r_cpg, "renesas,sh7750r-cpg",
+ sh7750r_cpg_init);
+CLK_OF_DECLARE_DRIVER(sh7751_cpg, "renesas,sh7751-cpg",
+ sh7751_cpg_init);
+CLK_OF_DECLARE_DRIVER(sh7751r_cpg, "renesas,sh7751r-cpg",
+ sh7751r_cpg_init);
+
+static int sh7750_cpg_probe(struct platform_device *pdev)
+{
+ u32 feature;
+
+ if (cpg_data)
+ return 0;
+ feature = *(u32 *)of_device_get_match_data(&pdev->dev);
+ return sh7750_cpg_setup(pdev->dev.of_node, feature);
+}
+
+static const struct of_device_id sh7750_cpg_of_match[] = {
+ { .compatible = "renesas,sh7750-cpg",
+ .data = &cpg_feature[CPG_SH7750] },
+ { .compatible = "renesas,sh7750s-cpg",
+ .data = &cpg_feature[CPG_SH7750S] },
+ { .compatible = "renesas,sh7750r-cpg",
+ .data = &cpg_feature[CPG_SH7750R] },
+ { .compatible = "renesas,sh7751-cpg",
+ .data = &cpg_feature[CPG_SH7751] },
+ { .compatible = "renesas,sh7751r-cpg",
+ .data = &cpg_feature[CPG_SH7751R] },
+ { }
+};
+
+static struct platform_driver sh7750_cpg_driver = {
+ .probe = sh7750_cpg_probe,
+ .driver = {
+ .name = "sh7750-cpg",
+ .of_match_table = sh7750_cpg_of_match,
+ },
+};
+builtin_platform_driver(sh7750_cpg_driver);
--
2.39.2


2024-04-04 05:04:47

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 19/37] dt-bindings: interrupt-controller: renesas,sh7751-irl-ext: Add json-schema

Renesas SH7751 external interrupt encoder json-schema.

Signed-off-by: Yoshinori Sato <[email protected]>
---
.../renesas,sh7751-irl-ext.yaml | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-irl-ext.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-irl-ext.yaml b/Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-irl-ext.yaml
new file mode 100644
index 000000000000..fc174c0467e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,sh7751-irl-ext.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/renesas,sh7751-irl-ext.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas SH7751 external interrupt encoder with enable regs.
+
+maintainers:
+ - Yoshinori Sato <[email protected]>
+
+description:
+ This is the generally used external interrupt encoder on SH7751 based boards.
+
+properties:
+ compatible:
+ items:
+ - const: renesas,sh7751-irl-ext
+
+ reg: true
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 2
+
+ '#address-cells':
+ const: 0
+
+ renesas,set-to-disable:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: Invert enable registers. Setting the bit to 0 enables interrupts.
+
+ renesas,enable-reg:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description: |
+ IRQ enable register bit mapping
+
+required:
+ - compatible
+ - reg
+ - interrupt-controller
+ - '#interrupt-cells'
+ - renesas,enable-reg
+
+additionalProperties: false
+
+examples:
+ - |
+ r2dintc: interrupt-controller@a4000000 {
+ compatible = "renesas,sh7751-irl-ext";
+ reg = <0xa4000000 0x02>;
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ renesas,enable-reg = <12 9 10 3 0 4 1 2 8 5 6 7 15 15 15 11>;
+ };
--
2.39.2


2024-04-04 05:05:08

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 20/37] serial: sh-sci: fix SH4 OF support.

- fix earlycon name.
- fix earlyprintk hung (NULL pointer reference).
- fix SERIAL_SH_SCI_EARLYCON enablement

Signed-off-by: Yoshinori Sato <[email protected]>
Reviewed-by: Geert Uytterhoeven <[email protected]>
---
drivers/tty/serial/Kconfig | 2 +-
drivers/tty/serial/sh-sci.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index ffcf4882b25f..dfe5fd436816 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -661,7 +661,7 @@ config SERIAL_SH_SCI_EARLYCON
depends on SERIAL_SH_SCI=y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
- default ARCH_RENESAS
+ default ARCH_RENESAS || SUPERH

config SERIAL_SH_SCI_DMA
bool "DMA support" if EXPERT
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index e512eaa57ed5..46466fb5a637 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -2717,7 +2717,7 @@ static int sci_remap_port(struct uart_port *port)
if (port->membase)
return 0;

- if (port->dev->of_node || (port->flags & UPF_IOREMAP)) {
+ if ((port->dev && port->dev->of_node) || (port->flags & UPF_IOREMAP)) {
port->membase = ioremap(port->mapbase, sport->reg_size);
if (unlikely(!port->membase)) {
dev_err(port->dev, "can't remap port#%d\n", port->line);
@@ -3545,8 +3545,8 @@ static int __init hscif_early_console_setup(struct earlycon_device *device,

OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup);
OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup);
-OF_EARLYCON_DECLARE(scif, "renesas,scif-r7s9210", rzscifa_early_console_setup);
-OF_EARLYCON_DECLARE(scif, "renesas,scif-r9a07g044", rzscifa_early_console_setup);
+OF_EARLYCON_DECLARE(rzscifa, "renesas,scif-r7s9210", rzscifa_early_console_setup);
+OF_EARLYCON_DECLARE(rzscifa, "renesas,scif-r9a07g044", rzscifa_early_console_setup);
OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup);
OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup);
OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup);
--
2.39.2


2024-04-04 05:05:49

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 23/37] dt-bindings: display: sm501 register definition helper

Miscellaneous Timing and Miscellaneous Control registers definition.

Signed-off-by: Yoshinori Sato <[email protected]>
---
include/dt-bindings/display/sm501.h | 76 +++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 include/dt-bindings/display/sm501.h

diff --git a/include/dt-bindings/display/sm501.h b/include/dt-bindings/display/sm501.h
new file mode 100644
index 000000000000..a6c6943642e4
--- /dev/null
+++ b/include/dt-bindings/display/sm501.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+#ifndef __DT_BINDING_DISPALY_SM501__
+#define __DT_BINDING_DISPALY_SM501__
+
+/* Miscellaneous Conntrol */
+#define SM501_MISC_CONTROL_PAD_24 0
+#define SM501_MISC_CONTROL_PAD_12 1
+#define SM501_MISC_CONTROL_PAD_8 2
+
+#define SM501_MISC_CONTROL_USBCLK_XTAL 0
+#define SM501_MISC_CONTROL_USBCLK_96MHZ 1
+#define SM501_MISC_CONTROL_USBCLK_48MHZ 2
+
+#define SM501_MISC_CONTROL_RFSH_8US 0
+#define SM501_MISC_CONTROL_RFSH_16US 1
+#define SM501_MISC_CONTROL_RFSH_32US 2
+#define SM501_MISC_CONTROL_RFSH_64US 3
+
+#define SM501_MISC_CONTROL_HOLD_EMPTY 0
+#define SM501_MISC_CONTROL_HOLD_8TR 1
+#define SM501_MISC_CONTROL_HOLD_16TR 2
+#define SM501_MISC_CONTROL_HOLD_24TR 3
+#define SM501_MISC_CONTROL_HOLD_32TR 4
+
+/* Miscellaneous timing */
+#define SM501_MISC_TIMING_EX_HOLD_0 0
+#define SM501_MISC_TIMING_EX_HOLD_16 1
+#define SM501_MISC_TIMING_EX_HOLD_32 2
+#define SM501_MISC_TIMING_EX_HOLD_48 3
+#define SM501_MISC_TIMING_EX_HOLD_64 4
+#define SM501_MISC_TIMING_EX_HOLD_80 5
+#define SM501_MISC_TIMING_EX_HOLD_96 6
+#define SM501_MISC_TIMING_EX_HOLD_112 7
+#define SM501_MISC_TIMING_EX_HOLD_128 8
+#define SM501_MISC_TIMING_EX_HOLD_144 9
+#define SM501_MISC_TIMING_EX_HOLD_160 10
+#define SM501_MISC_TIMING_EX_HOLD_176 11
+#define SM501_MISC_TIMING_EX_HOLD_192 12
+#define SM501_MISC_TIMING_EX_HOLD_208 13
+#define SM501_MISC_TIMING_EX_HOLD_224 14
+#define SM501_MISC_TIMING_EX_HOLD_240 15
+
+#define SM501_MISC_TIMING_XC_INTERNAL 0
+#define SM501_MISC_TIMING_XC_HCLK 1
+#define SM501_MISC_TIMING_XC_GPIO 2
+
+#define SM501_MISC_TIMING_SM_DIV1 0
+#define SM501_MISC_TIMING_SM_DIV2 1
+#define SM501_MISC_TIMING_SM_DIV4 2
+#define SM501_MISC_TIMING_SM_DIV8 3
+#define SM501_MISC_TIMING_SM_DIV16 4
+#define SM501_MISC_TIMING_SM_DIV32 5
+#define SM501_MISC_TIMING_SM_DIV64 6
+#define SM501_MISC_TIMING_SM_DIV128 7
+#define SM501_MISC_TIMING_SM_DIV3 8
+#define SM501_MISC_TIMING_SM_DIV6 9
+#define SM501_MISC_TIMING_SM_DIV12 10
+#define SM501_MISC_TIMING_SM_DIV24 11
+#define SM501_MISC_TIMING_SM_DIV48 12
+#define SM501_MISC_TIMING_SM_DIV96 13
+#define SM501_MISC_TIMING_SM_DIV192 14
+#define SM501_MISC_TIMING_SM_DIV384 15
+
+#define SM501_MISC_TIMING_DIV336MHZ 0
+#define SM501_MISC_TIMING_DIV288MHZ 1
+#define SM501_MISC_TIMING_DIV240MHZ 2
+#define SM501_MISC_TIMING_DIV192MHZ 3
+
+#define SM501_MISC_TIMING_DELAY_NONE 0
+#define SM501_MISC_TIMING_DELAY_0_5 1
+#define SM501_MISC_TIMING_DELAY_1_0 2
+#define SM501_MISC_TIMING_DELAY_1_5 3
+#define SM501_MISC_TIMING_DELAY_2_0 4
+#define SM501_MISC_TIMING_DELAY_2_5 5
+
+#endif
--
2.39.2


2024-04-04 05:05:10

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 16/37] irqchip: Add SH7751 INTC driver

Renesas SH7751 Internal interrupt controller driver.

Signed-off-by: Yoshinori Sato <[email protected]>
---
drivers/irqchip/Kconfig | 8 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-renesas-sh7751.c | 282 +++++++++++++++++++++++++++
3 files changed, 291 insertions(+)
create mode 100644 drivers/irqchip/irq-renesas-sh7751.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 72c07a12f5e1..33badb5b4f00 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -690,4 +690,12 @@ config SUNPLUS_SP7021_INTC
chained controller, routing all interrupt source in P-Chip to
the primary controller on C-Chip.

+config RENESAS_SH7751_INTC
+ bool "Renesas SH7751 Interrupt Controller"
+ depends on SH_DEVICE_TREE || COMPILE_TEST
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ Support for the Renesas SH7751 On-chip interrupt controller.
+ And external interrupt encoder for some targets.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ec4a18380998..51855034a895 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -121,3 +121,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o
obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o
obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o
+obj-$(CONFIG_RENESAS_SH7751_INTC) += irq-renesas-sh7751.o
diff --git a/drivers/irqchip/irq-renesas-sh7751.c b/drivers/irqchip/irq-renesas-sh7751.c
new file mode 100644
index 000000000000..91d6dc3ed04c
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-sh7751.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas SH7751 interrupt controller driver
+ *
+ * Copyright 2023 Yoshinori Sato <[email protected]>
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <dt-bindings/interrupt-controller/renesas,sh7751-intc.h>
+
+struct ipr {
+ u16 off;
+ u16 idx;
+};
+
+struct sh7751_intc_priv {
+ const struct ipr *iprmap;
+ void __iomem *base;
+ void __iomem *intpri00;
+ bool irlm;
+};
+
+enum {
+ R_ICR = 0x00,
+ R_IPR = 0x04,
+ R_INTPRI00 = 0x00,
+ R_INTREQ00 = 0x20,
+ R_INTMSK00 = 0x40,
+ R_INTMSKCLR00 = 0x60,
+};
+
+#define ICR_IRLM BIT(7)
+
+/*
+ * SH7751 IRQ mapping
+ * IRQ16 - 63: Group0 - IPRA to IPRD
+ * IRQ16 - 31: external IRL input (ICR.IRLM is 0)
+ * IRQ80 - 92: Group1 - INTPRI00
+ */
+#define IRQ_START 16
+#define MAX_IRL (IRQ_START + NR_IRL)
+#define GRP0_IRQ_END 63
+#define GRP1_IRQ_START 80
+#define IRQ_END 92
+
+#define NR_IPRMAP0 (GRP0_IRQ_END - IRQ_START + 1)
+#define NR_IPRMAP1 (IRQ_END - GRP1_IRQ_START)
+#define IPR_PRI_MASK 0x000f
+
+#define IPRA 0
+#define IPRB 4
+#define IPRC 8
+#define IPRD 12
+#define INTPRI00 256
+#define IPR_B12 12
+#define IPR_B8 8
+#define IPR_B4 4
+#define IPR_B0 0
+
+/* SH7751 EVT to IPR mapping table */
+static const struct ipr sh7751_iprmap[] = {
+ [evt2irq(0x240)] = {IPRD, IPR_B12}, /* IRL0 (ICR.IRLM=1) */
+ [evt2irq(0x2a0)] = {IPRD, IPR_B8}, /* IRL1 (ICR.IRLM=1) */
+ [evt2irq(0x300)] = {IPRD, IPR_B4}, /* IRL2 (ICR.IRLM=1) */
+ [evt2irq(0x360)] = {IPRD, IPR_B0}, /* IRL3 (ICR.IRLM=1) */
+ [evt2irq(0x400)] = {IPRA, IPR_B12}, /* TMU0 */
+ [evt2irq(0x420)] = {IPRA, IPR_B8}, /* TMU1 */
+ [evt2irq(0x440)] = {IPRA, IPR_B4}, /* TMU2 TNUI */
+ [evt2irq(0x460)] = {IPRA, IPR_B4}, /* TMU2 TICPI */
+ [evt2irq(0x480)] = {IPRA, IPR_B0}, /* RTC ATI */
+ [evt2irq(0x4a0)] = {IPRA, IPR_B0}, /* RTC PRI */
+ [evt2irq(0x4c0)] = {IPRA, IPR_B0}, /* RTC CUI */
+ [evt2irq(0x4e0)] = {IPRB, IPR_B4}, /* SCI ERI */
+ [evt2irq(0x500)] = {IPRB, IPR_B4}, /* SCI RXI */
+ [evt2irq(0x520)] = {IPRB, IPR_B4}, /* SCI TXI */
+ [evt2irq(0x540)] = {IPRB, IPR_B4}, /* SCI TEI */
+ [evt2irq(0x560)] = {IPRB, IPR_B12}, /* WDT */
+ [evt2irq(0x580)] = {IPRB, IPR_B8}, /* REF RCMI */
+ [evt2irq(0x5a0)] = {IPRB, IPR_B4}, /* REF ROVI */
+ [evt2irq(0x600)] = {IPRC, IPR_B0}, /* H-UDI */
+ [evt2irq(0x620)] = {IPRC, IPR_B12}, /* GPIO */
+ [evt2irq(0x640)] = {IPRC, IPR_B8}, /* DMAC DMTE0 */
+ [evt2irq(0x660)] = {IPRC, IPR_B8}, /* DMAC DMTE1 */
+ [evt2irq(0x680)] = {IPRC, IPR_B8}, /* DMAC DMTE2 */
+ [evt2irq(0x6a0)] = {IPRC, IPR_B8}, /* DMAC DMTE3 */
+ [evt2irq(0x6c0)] = {IPRC, IPR_B8}, /* DMAC DMAE */
+ [evt2irq(0x700)] = {IPRC, IPR_B4}, /* SCIF ERI */
+ [evt2irq(0x720)] = {IPRC, IPR_B4}, /* SCIF RXI */
+ [evt2irq(0x740)] = {IPRC, IPR_B4}, /* SCIF BRI */
+ [evt2irq(0x760)] = {IPRC, IPR_B4}, /* SCIF TXI */
+ [evt2irq(0x780)] = {IPRC, IPR_B8}, /* DMAC DMTE4 */
+ [evt2irq(0x7a0)] = {IPRC, IPR_B8}, /* DMAC DMTE5 */
+ [evt2irq(0x7c0)] = {IPRC, IPR_B8}, /* DMAC DMTE6 */
+ [evt2irq(0x7e0)] = {IPRC, IPR_B8}, /* DMAC DMTE7 */
+ [evt2irq(0xa00)] = {INTPRI00, IPR_B0}, /* PCIC PCISERR */
+ [evt2irq(0xa20)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA3 */
+ [evt2irq(0xa40)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA2 */
+ [evt2irq(0xa60)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA1 */
+ [evt2irq(0xa80)] = {INTPRI00, IPR_B4}, /* PCIC PCIDMA0 */
+ [evt2irq(0xaa0)] = {INTPRI00, IPR_B4}, /* PCIC PCIPWON */
+ [evt2irq(0xac0)] = {INTPRI00, IPR_B4}, /* PCIC PCIPWDWN */
+ [evt2irq(0xae0)] = {INTPRI00, IPR_B4}, /* PCIC PCIERR */
+ [evt2irq(0xb00)] = {INTPRI00, IPR_B8}, /* TMU3 */
+ [evt2irq(0xb80)] = {INTPRI00, IPR_B12}, /* TMU4 */
+};
+
+/*
+ * IPR registers have 4bit priority x 4 entry (16bits)
+ */
+static void update_ipr(struct sh7751_intc_priv *priv, unsigned int irq, u16 pri)
+{
+ const struct ipr *ipr = NULL;
+ void __iomem *ipr_base;
+ unsigned int offset;
+ u16 mask;
+
+ ipr = priv->iprmap + irq;
+ if (irq < GRP1_IRQ_START) {
+ /* Group0 */
+ ipr_base = priv->base + R_IPR;
+ offset = ipr->off;
+ } else {
+ /* Group1 */
+ ipr_base = priv->intpri00;
+ offset = ipr->off - INTPRI00;
+ }
+ mask = ~(IPR_PRI_MASK << ipr->idx);
+ pri = (pri & IPR_PRI_MASK) << ipr->idx;
+ mask &= __raw_readw(ipr_base + offset);
+ __raw_writew(mask | pri, ipr_base + offset);
+}
+
+static inline bool is_valid_irq(unsigned int irq)
+{
+ /* IRQ16 - 63 */
+ if (irq >= IRQ_START && irq < IRQ_START + NR_IPRMAP0)
+ return true;
+ /* IRQ80 - 92 */
+ if (irq >= GRP1_IRQ_START && irq <= IRQ_END)
+ return true;
+ return false;
+}
+
+static inline struct sh7751_intc_priv *irq_data_to_priv(struct irq_data *data)
+{
+ return data->domain->host_data;
+}
+
+/* Interrupt unmask priority is 1, mask priority is 0 */
+#define PRI_ENABLE 1
+#define PRI_DISABLE 0
+static void endisable_irq(struct irq_data *data, bool enable)
+{
+ struct sh7751_intc_priv *priv;
+ unsigned int irq;
+
+ priv = irq_data_to_priv(data);
+
+ irq = irqd_to_hwirq(data);
+ if (!is_valid_irq(irq)) {
+ /* IRQ out of range */
+ pr_warn_once("%s: IRQ %u is out of range\n", __FILE__, irq);
+ return;
+ }
+
+ if (irq <= MAX_IRL && !priv->irlm) {
+ /* IRL encoded external interrupt */
+ /* enable and disable from SR.IMASK */
+ update_sr_imask(irq - IRQ_START, enable);
+ } else {
+ /* Internal peripheral interrupt */
+ /* enable and disable from interrupt priority */
+ update_ipr(priv, irq, enable ? PRI_ENABLE : PRI_DISABLE);
+ }
+}
+
+static void sh7751_mask_irq(struct irq_data *data)
+{
+ endisable_irq(data, false);
+}
+
+static void sh7751_unmask_irq(struct irq_data *data)
+{
+ endisable_irq(data, true);
+}
+
+static const struct irq_chip sh7751_irq_chip = {
+ .name = "SH7751-INTC",
+ .irq_unmask = sh7751_unmask_irq,
+ .irq_mask = sh7751_mask_irq,
+};
+
+static int irq_sh7751_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &sh7751_irq_chip, handle_level_irq);
+ irq_get_irq_data(virq)->chip_data = h->host_data;
+ irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
+ return 0;
+}
+
+static int irq_sh7751_xlate(struct irq_domain *d, struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ if (WARN_ON(intsize < 1))
+ return -EINVAL;
+ *out_hwirq = evt2irq(intspec[0]);
+ *out_type = IRQ_TYPE_NONE;
+ return 0;
+}
+
+static const struct irq_domain_ops irq_ops = {
+ .map = irq_sh7751_map,
+ .xlate = irq_sh7751_xlate,
+};
+
+static int __init shintc_of_init(struct device_node *intc, struct device_node *parent,
+ const struct ipr *iprmap)
+{
+ struct sh7751_intc_priv *priv;
+ void __iomem *base, *base2;
+ struct irq_domain *domain;
+ u16 icr;
+ int ret;
+
+ priv = kzalloc(sizeof(struct sh7751_intc_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ base = of_iomap(intc, 0);
+ base2 = of_iomap(intc, 1);
+ if (!base || !base2) {
+ pr_err("%pOFP: Invalid register definition\n", intc);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ priv->base = base;
+ priv->intpri00 = base2;
+ priv->iprmap = iprmap;
+
+ if (of_property_read_bool(intc, "renesas,irlm")) {
+ priv->irlm = true;
+ icr = __raw_readw(priv->base + R_ICR);
+ icr |= ICR_IRLM;
+ __raw_writew(icr, priv->base + R_ICR);
+ }
+
+ domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, priv);
+ if (domain == NULL) {
+ pr_err("%pOFP: cannot initialize irq domain\n", intc);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ irq_set_default_host(domain);
+ pr_info("%pOFP: SH7751 Interrupt controller (%s external IRQ)",
+ intc, priv->irlm ? "4 lines" : "15 level");
+ return 0;
+
+error:
+ if (base)
+ iounmap(base);
+ if (base2)
+ iounmap(base);
+ kfree(priv);
+ return ret;
+}
+
+static int __init sh7751_intc_of_init(struct device_node *intc,
+ struct device_node *parent)
+{
+ return shintc_of_init(intc, parent, sh7751_iprmap);
+}
+
+IRQCHIP_DECLARE(sh_7751_intc, "renesas,sh7751-intc", sh7751_intc_of_init);
--
2.39.2


2024-04-04 05:05:37

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 18/37] irqchip: SH7751 external interrupt encoder with enable gate.

SH7751 have 15 level external interrupt.
It is typically connected to the CPU through a priority encoder
that can suppress requests.
This driver provides a way to control those hardware with irqchip.

Signed-off-by: Yoshinori Sato <[email protected]>
---
drivers/irqchip/Kconfig | 7 +
drivers/irqchip/Makefile | 2 +
drivers/irqchip/irq-renesas-sh7751irl.c | 221 ++++++++++++++++++++++++
3 files changed, 230 insertions(+)
create mode 100644 drivers/irqchip/irq-renesas-sh7751irl.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 33badb5b4f00..7670fcd6757d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -698,4 +698,11 @@ config RENESAS_SH7751_INTC
Support for the Renesas SH7751 On-chip interrupt controller.
And external interrupt encoder for some targets.

+config RENESAS_SH7751IRL_INTC
+ bool "Renesas SH7751 based target IRL encoder support."
+ depends on RENESAS_SH7751_INTC
+ help
+ Support for External Interrupt encoder
+ on the some Renesas SH7751 based target.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 51855034a895..bc21d65441f2 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -122,3 +122,5 @@ obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o
obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o
obj-$(CONFIG_RENESAS_SH7751_INTC) += irq-renesas-sh7751.o
+obj-$(CONFIG_RENESAS_SH7751IRL_INTC) += irq-renesas-sh7751irl.o
+
diff --git a/drivers/irqchip/irq-renesas-sh7751irl.c b/drivers/irqchip/irq-renesas-sh7751irl.c
new file mode 100644
index 000000000000..5990f2cd9a3d
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-sh7751irl.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SH7751 based board external interrupt level encoder driver
+ * (Renesas RTS7751R2D / IO DATA DEVICE LANDISK, USL-5P)
+ *
+ * Copyright (C) 2023 Yoshinori Sato
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+struct sh7751irl_intc_priv {
+ struct irq_domain *irq_domain;
+ void __iomem *base;
+ unsigned int width;
+ bool invert;
+ u32 enable_bit[NR_IRL];
+};
+
+static inline unsigned long get_reg(void __iomem *addr, unsigned int w)
+{
+ switch (w) {
+ case 8:
+ return __raw_readb(addr);
+ case 16:
+ return __raw_readw(addr);
+ case 32:
+ return __raw_readl(addr);
+ default:
+ /* The size is checked when reading the properties. */
+ pr_err("%s: Invalid width %d", __FILE__, w);
+ return 0;
+ }
+}
+
+static inline void set_reg(void __iomem *addr, unsigned int w, unsigned long val)
+{
+ switch (w) {
+ case 8:
+ __raw_writeb(val, addr);
+ break;
+ case 16:
+ __raw_writew(val, addr);
+ break;
+ case 32:
+ __raw_writel(val, addr);
+ break;
+ default:
+ pr_err("%s: Invalid width %d", __FILE__, w);
+ }
+}
+
+static inline struct sh7751irl_intc_priv *irq_data_to_priv(struct irq_data *data)
+{
+ return data->domain->host_data;
+}
+
+static void irl_endisable(struct irq_data *data, unsigned int enable)
+{
+ struct sh7751irl_intc_priv *priv;
+ unsigned long val;
+ unsigned int irl;
+
+ priv = irq_data_to_priv(data);
+ irl = irqd_to_hwirq(data) - IRL_BASE_IRQ;
+
+ if (irl < NR_IRL && priv->enable_bit[irl] < priv->width) {
+ if (priv->invert)
+ enable = !enable;
+
+ val = get_reg(priv->base, priv->width);
+ if (enable)
+ set_bit(priv->enable_bit[irl], &val);
+ else
+ clear_bit(priv->enable_bit[irl], &val);
+ set_reg(priv->base, priv->width, val);
+ } else {
+ pr_err("%s: Invalid register define in IRL %u", __FILE__, irl);
+ }
+}
+
+static void sh7751irl_intc_disable_irq(struct irq_data *data)
+{
+ irl_endisable(data, 0);
+}
+
+static void sh7751irl_intc_enable_irq(struct irq_data *data)
+{
+ irl_endisable(data, 1);
+}
+
+static struct irq_chip sh7751irl_intc_chip = {
+ .name = "SH7751IRL-INTC",
+ .irq_enable = sh7751irl_intc_enable_irq,
+ .irq_disable = sh7751irl_intc_disable_irq,
+};
+
+static int sh7751irl_intc_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &sh7751irl_intc_chip, handle_level_irq);
+ irq_get_irq_data(virq)->chip_data = h->host_data;
+ irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
+ return 0;
+}
+
+static int sh7751irl_intc_translate(struct irq_domain *domain,
+ struct irq_fwspec *fwspec, unsigned long *hwirq,
+ unsigned int *type)
+{
+ if (fwspec->param[0] > NR_IRL)
+ return -EINVAL;
+
+ switch (fwspec->param_count) {
+ case 2:
+ *type = fwspec->param[1];
+ fallthrough;
+ case 1:
+ *hwirq = fwspec->param[0] + IRL_BASE_IRQ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct irq_domain_ops sh7751irl_intc_domain_ops = {
+ .map = sh7751irl_intc_map,
+ .translate = sh7751irl_intc_translate,
+};
+
+static int __init load_irl_bit(struct device_node *node, struct sh7751irl_intc_priv *priv)
+{
+ struct property *enable_map;
+ const __be32 *p;
+ u32 nr_bits, bit;
+ u32 irl;
+ int ret;
+
+ /* Fill in unused */
+ memset(priv->enable_bit, ~0, sizeof(priv->enable_bit));
+
+ enable_map = of_find_property(node, "renesas,enable-reg", &nr_bits);
+ if (IS_ERR(enable_map))
+ return PTR_ERR(enable_map);
+
+ nr_bits /= sizeof(u32);
+ if (nr_bits > priv->width)
+ return -EINVAL;
+
+ ret = nr_bits;
+ p = NULL;
+ for (bit = nr_bits; bit > 0; bit--) {
+ p = of_prop_next_u32(enable_map, p, &irl);
+ if (p == NULL || irl > NR_IRL)
+ return -EINVAL;
+ if (irl == NR_IRL)
+ /* IRL15 is unassined bit */
+ continue;
+ priv->enable_bit[irl] = bit - 1;
+ }
+ return ret;
+}
+
+static int __init sh7751irl_init(struct device_node *node, struct device_node *parent)
+{
+ struct sh7751irl_intc_priv *priv;
+ struct resource res;
+ struct irq_domain *d;
+ void __iomem *base;
+ int ret = 0;
+
+ if (of_address_to_resource(node, 0, &res))
+ return -EINVAL;
+ if (resource_size(&res) > 4)
+ return -EINVAL;
+
+ base = ioremap(res.start, resource_size(&res));
+ if (!base)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = base;
+ priv->width = 8 << resource_size(&res);
+
+ ret = load_irl_bit(node, priv);
+ if (ret < 0) {
+ pr_err("%pOFP: Invalid register define.\n", node);
+ goto error;
+ }
+
+ d = irq_domain_add_tree(node, &sh7751irl_intc_domain_ops, priv);
+ if (d == NULL) {
+ pr_err("%pOFP: cannot initialize irq domain\n", node);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ priv->irq_domain = d;
+ irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
+ pr_info("%pOFP: SH7751 External Interrupt encoder (input=%d)", node, ret);
+ return 0;
+error:
+ kfree(priv);
+ return ret;
+}
+
+IRQCHIP_DECLARE(renesas_sh7751_irl, "renesas,sh7751-irl-ext", sh7751irl_init);
--
2.39.2


2024-04-04 05:02:38

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 10/37] sh: Common PCI Framework driver support.

Add New OF based PCI Host driver.
This driver conflicts some point in legacy PCI driver.
To resolve the conflict, I made some changes to the legacy driver.

Signed-off-by: Yoshinori Sato <[email protected]>
---
arch/sh/include/asm/io.h | 6 ++++++
arch/sh/include/asm/pci.h | 4 ++++
arch/sh/kernel/iomap.c | 18 ++++++++++++++++++
3 files changed, 28 insertions(+)

diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h
index 5c544cf5201b..29b5f996cde3 100644
--- a/arch/sh/include/asm/io.h
+++ b/arch/sh/include/asm/io.h
@@ -20,6 +20,7 @@
#include <asm/page.h>
#include <linux/pgtable.h>
#include <asm-generic/iomap.h>
+#include <linux/ioport.h>

#define __IO_PREFIX generic
#include <asm/io_generic.h>
@@ -310,4 +311,9 @@ unsigned long long poke_real_address_q(unsigned long long addr,
int valid_phys_addr_range(phys_addr_t addr, size_t size);
int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);

+#if defined(CONFIG_PCI) && !defined(CONFIG_GENERIC_IOMAP)
+#define pci_remap_iospace pci_remap_iospace
+int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
+#endif
+
#endif /* __ASM_SH_IO_H */
diff --git a/arch/sh/include/asm/pci.h b/arch/sh/include/asm/pci.h
index 54c30126ea17..92b3bd604319 100644
--- a/arch/sh/include/asm/pci.h
+++ b/arch/sh/include/asm/pci.h
@@ -2,6 +2,7 @@
#ifndef __ASM_SH_PCI_H
#define __ASM_SH_PCI_H

+#ifndef CONFIG_SH_DEVICE_TREE
/* Can be used to override the logic in pci_scan_bus for skipping
already-configured bus numbers - to be used for buggy BIOSes
or architectures with incomplete PCI setup by the loader */
@@ -88,4 +89,7 @@ static inline int pci_proc_domain(struct pci_bus *bus)
return hose->need_domain_info;
}

+#else /* CONFIG_SH_DEVICE_TREE */
+#include <asm-generic/pci.h>
+#endif
#endif /* __ASM_SH_PCI_H */
diff --git a/arch/sh/kernel/iomap.c b/arch/sh/kernel/iomap.c
index 0a0dff4e66de..d1b8e496ca23 100644
--- a/arch/sh/kernel/iomap.c
+++ b/arch/sh/kernel/iomap.c
@@ -160,3 +160,21 @@ void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count)
mmio_outsl(addr, src, count);
}
EXPORT_SYMBOL(iowrite32_rep);
+
+#if defined(pci_remap_iospace)
+int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
+{
+ unsigned long vaddr = res->start;
+
+ if (!(res->flags & IORESOURCE_IO))
+ return -EINVAL;
+
+ if (res->end > IO_SPACE_LIMIT)
+ return -EINVAL;
+
+ __set_io_port_base(phys_addr);
+ return vmap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
+ pgprot_device(PAGE_KERNEL));
+}
+EXPORT_SYMBOL(pci_remap_iospace);
+#endif
--
2.39.2


2024-04-04 05:02:57

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 11/37] pci: pci-sh7751: Add SH7751 PCI driver

Renesas SH7751 CPU Internal PCI Controller driver.

Signed-off-by: Yoshinori Sato <[email protected]>
---
drivers/pci/controller/Kconfig | 9 +
drivers/pci/controller/Makefile | 1 +
drivers/pci/controller/pci-sh7751.c | 342 ++++++++++++++++++++++++++++
3 files changed, 352 insertions(+)
create mode 100644 drivers/pci/controller/pci-sh7751.c

diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index e534c02ee34f..a2fd917a2e03 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -353,6 +353,15 @@ config PCIE_XILINX_CPM
Say 'Y' here if you want kernel support for the
Xilinx Versal CPM host bridge.

+config PCI_SH7751
+ bool "Renesas SH7751 PCI controller"
+ depends on OF
+ depends on CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7751R || COMPILE_TEST
+ select PCI_HOST_COMMON
+ help
+ Say 'Y' here if you want kernel to support the Renesas SH7751 PCI
+ Host Bridge driver.
+
source "drivers/pci/controller/cadence/Kconfig"
source "drivers/pci/controller/dwc/Kconfig"
source "drivers/pci/controller/mobiveil/Kconfig"
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index f2b19e6174af..aa97e5d74e58 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o
obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o
obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o
+obj-$(CONFIG_PCI_SH7751) += pci-sh7751.o

# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
obj-y += dwc/
diff --git a/drivers/pci/controller/pci-sh7751.c b/drivers/pci/controller/pci-sh7751.c
new file mode 100644
index 000000000000..a5340689f737
--- /dev/null
+++ b/drivers/pci/controller/pci-sh7751.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SH7751 PCI driver
+ * Copyright (C) 2023 Yoshinori Sato
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/pci-ecam.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/dma-direct.h>
+#include <asm/addrspace.h>
+
+/* PCICR and PCICLKCR write enable magic key */
+#define PCIC_WE_KEY (0xa5 << 24)
+
+/* PCIC registers */
+/* 0x0000 - 0x00ff mapped to PCI device configuration space */
+#define PCIC_PCICR 0x100 /* PCI Control Register */
+#define PCIC_PCICR_TRSB BIT(9) /* Target Read Single */
+#define PCIC_PCICR_BSWP BIT(8) /* Target Byte Swap */
+#define PCIC_PCICR_PLUP BIT(7) /* Enable PCI Pullup */
+#define PCIC_PCICR_ARBM BIT(6) /* PCI Arbitration Mode */
+#define PCIC_PCICR_MD10 BIT(5) /* MD10 status */
+#define PCIC_PCICR_MD9 BIT(4) /* MD9 status */
+#define PCIC_PCICR_SERR BIT(3) /* SERR output assert */
+#define PCIC_PCICR_INTA BIT(2) /* INTA output assert */
+#define PCIC_PCICR_PRST BIT(1) /* PCI Reset Assert */
+#define PCIC_PCICR_CFIN BIT(0) /* Central Fun. Init Done */
+
+#define PCIC_PCILSR0 0x104 /* PCI Local Space Register0 */
+#define PCIC_PCILSR1 0x108 /* PCI Local Space Register1 */
+#define PCIC_PCILAR0 0x10c /* PCI Local Addr Register1 */
+#define PCIC_PCILAR1 0x110 /* PCI Local Addr Register1 */
+#define PCIC_PCIINT 0x114 /* PCI Interrupt Register */
+#define PCIC_PCIINTM 0x118 /* PCI Interrupt Mask */
+#define PCIC_PCIALR 0x11c /* Error Address Register */
+#define PCIC_PCICLR 0x120 /* Error Command/Data */
+#define PCIC_PCIAINT 0x130 /* Arbiter Interrupt Register */
+#define PCIC_PCIAINTM 0x134 /* Arbiter Int. Mask Register */
+#define PCIC_PCIBMLR 0x138 /* Error Bus Master Register */
+#define PCIC_PCIDMABT 0x140 /* DMA Transfer Arb. Register */
+#define PCIC_PCIPAR 0x1c0 /* PIO Address Register */
+#define PCIC_PCIMBR 0x1c4 /* Memory Base Address */
+#define PCIC_PCIIOBR 0x1c8 /* I/O Base Address Register */
+
+#define PCIC_PCIPINT 0x1cc /* Power Mgmnt Int. Register */
+#define PCIC_PCIPINT_D3 BIT(1) /* D3 Pwr Mgmt. Interrupt */
+#define PCIC_PCIPINT_D0 BIT(0) /* D0 Pwr Mgmt. Interrupt */
+
+#define PCIC_PCIPINTM 0x1d0 /* Power Mgmnt Mask Register */
+#define PCIC_PCICLKR 0x1d4 /* Clock Ctrl. Register */
+#define PCIC_PCIBCR1 0x1e0 /* Memory BCR1 Register */
+#define PCIC_PCIBCR2 0x1e4 /* Memory BCR2 Register */
+#define PCIC_PCIWCR1 0x1e8 /* Wait Control 1 Register */
+#define PCIC_PCIWCR2 0x1ec /* Wait Control 2 Register */
+#define PCIC_PCIWCR3 0x1f0 /* Wait Control 3 Register */
+#define PCIC_PCIMCR 0x1f4 /* Memory Control Register */
+#define PCIC_PCIBCR3 0x1f8 /* Memory BCR3 Register */
+#define PCIC_PCIPDR 0x220 /* Port IO Data Register */
+
+/* PCI IDs */
+/* Hitachi is the company that led to Renesas. */
+/* The SH7751 was designed by Hitachi, so it has a Hitachi ID. */
+#define PCI_VENDOR_ID_HITACHI 0x1054
+#define PCI_DEVICE_ID_SH7751 0x3505
+#define PCI_DEVICE_ID_SH7751R 0x350e
+
+/* BSC registers */
+/* Copy BSC setting to PCI BSC */
+#define BSC_BCR1 0x0000
+#define BSC_BCR1_SLAVE BIT(30)
+#define BSC_BCR1_BRQEN BIT(19)
+#define BSC_BCR2 0x0004
+#define BSC_BCR3 0x0050
+#define BSC_WCR1 0x0008
+#define BSC_WCR2 0x000c
+#define BSC_WCR3 0x0010
+#define BSC_MCR 0x0014
+#define BSC_MCR_MRSET BIT(30)
+#define BSC_MCR_RFSH BIT(2)
+
+/* PCIC access wrapper */
+#define pcic_writel(val, base, reg) writel(val, base + (reg))
+#define pcic_readl(base, reg) readl(base + (reg))
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ */
+#define IO_REGION_BASE 0x1000
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+ resource_size_t size, resource_size_t align)
+{
+ resource_size_t start = res->start;
+
+ if (res->flags & IORESOURCE_IO) {
+ if (start < PCIBIOS_MIN_IO + IO_REGION_BASE)
+ start = PCIBIOS_MIN_IO + IO_REGION_BASE;
+
+ /*
+ * Put everything into 0x00-0xff region modulo 0x400.
+ */
+ if (start & 0x300)
+ start = (start + 0x3ff) & ~0x3ff;
+ }
+
+ return start;
+}
+
+static int setup_pci_bsc(struct device *dev, void __iomem *pcic,
+ void __iomem *bsc, unsigned int area, bool bcr3)
+{
+ u32 word;
+
+ word = __raw_readl(bsc + BSC_BCR1);
+ /* check BCR for SDRAM in area */
+ if (((word >> area) & 1) == 0) {
+ dev_err(dev, "Area %u is not configured for SDRAM. BCR1=0x%x\n",
+ area, word);
+ return -EINVAL;
+ }
+ word |= BSC_BCR1_SLAVE; /* PCIC BSC is slave only */
+ pcic_writel(word, pcic, PCIC_PCIBCR1);
+
+ word = __raw_readw(bsc + BSC_BCR2);
+ /* check BCR2 for 32bit SDRAM interface*/
+ if (((word >> (area << 1)) & 0x3) != 0x3) {
+ dev_err(dev, "Area %u is not 32 bit SDRAM. BCR2=0x%x\n",
+ area, word);
+ return -EINVAL;
+ }
+ pcic_writel(word, pcic, PCIC_PCIBCR2);
+
+ if (bcr3) {
+ /* BCR3 have only SH7751R */
+ word = __raw_readw(bsc + BSC_BCR3);
+ pcic_writel(word, pcic, PCIC_PCIBCR3);
+ }
+
+ /* configure the wait control registers */
+ word = __raw_readl(bsc + BSC_WCR1);
+ pcic_writel(word, pcic, PCIC_PCIWCR1);
+ word = __raw_readl(bsc + BSC_WCR2);
+ pcic_writel(word, pcic, PCIC_PCIWCR2);
+ word = __raw_readl(bsc + BSC_WCR3);
+ pcic_writel(word, pcic, PCIC_PCIWCR3);
+ word = __raw_readl(bsc + BSC_MCR);
+ /* Clear MRSET and RFSH bit */
+ word &= ~(BSC_MCR_MRSET | BSC_MCR_RFSH);
+ pcic_writel(word, pcic, PCIC_PCIMCR);
+
+ return 0;
+}
+
+#define NUM_AREA 7
+static int set_pci_ranges(struct device *dev,
+ void __iomem *pcic, void __iomem *bsc, bool bcr3)
+{
+ struct resource_entry *dma, *tmp;
+ struct pci_host_bridge *bridge;
+ u32 bsc_done[NUM_AREA];
+ unsigned int la;
+
+ bridge = dev_get_drvdata(dev);
+ pcic_writel(0, pcic, PCIC_PCILAR0);
+ pcic_writel(0, pcic, PCIC_PCILAR1);
+ la = 0;
+ memset(&bsc_done, 0, sizeof(bsc_done));
+ resource_list_for_each_entry_safe(dma, tmp, &bridge->dma_ranges) {
+ struct resource *res = dma->res;
+ unsigned int area;
+ u32 word;
+
+ switch (resource_type(res)) {
+ case IORESOURCE_IO:
+ /* BAR0 is I/O space */
+ word = res->start | 1;
+ pcic_writel(word, pcic, PCI_BASE_ADDRESS_0);
+ word = pcic_readl(pcic, PCI_COMMAND);
+ word |= PCI_COMMAND_IO;
+ pcic_writel(word, pcic, PCI_COMMAND);
+ break;
+ case IORESOURCE_MEM:
+ if (la > 4) {
+ dev_err(dev, "Invalid range definition.\n");
+ return -EINVAL;
+ }
+ area = (res->start >> 26) & 0x07;
+ word = res->end - res->start;
+ if (area >= NUM_AREA) {
+ /* Area 7 is reserved. */
+ dev_info(dev, "Invalid local address 0x%08x. Ignore it.\n",
+ res->start);
+ break;
+ }
+ pcic_writel(res->start, pcic, PCI_BASE_ADDRESS_1 + la);
+ /* if dummy entry, skip BSC setup */
+ if (word < 4)
+ break;
+ /* BAR1 is local area 0, BAR2 is local area 1 */
+ pcic_writel(word, pcic, PCIC_PCILSR0 + la);
+ word = P2SEGADDR(res->start);
+ pcic_writel(word, pcic, PCIC_PCILAR0 + la);
+ la += 4;
+ if (!bsc_done[area]) {
+ /* check BCR for SDRAM in specified area. And setup PCI BSC. */
+ if (setup_pci_bsc(dev, pcic, bsc, area, bcr3))
+ return -EINVAL;
+ bsc_done[area] = 1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int sh7751_pci_probe(struct platform_device *pdev)
+{
+ struct resource *res, *bscres;
+ void __iomem *pcic;
+ void __iomem *bsc;
+ u16 vid, did;
+ u32 word;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+ pcic = ioremap(res->start, res->end - res->start + 1);
+
+ bscres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ bsc = devm_ioremap_resource(&pdev->dev, bscres);
+ if (IS_ERR(bsc))
+ return PTR_ERR(bsc);
+
+ /* check for SH7751/SH7751R hardware */
+ word = pcic_readl(pcic, PCI_VENDOR_ID);
+ vid = word & 0xffff;
+ did = word >> 16;
+ if ((vid != PCI_VENDOR_ID_HITACHI) ||
+ ((did != PCI_DEVICE_ID_SH7751) &&
+ (did != PCI_DEVICE_ID_SH7751R))) {
+ dev_err(&pdev->dev, "This is not an SH7751(R)\n");
+ return -ENODEV;
+ }
+ dev_info(&pdev->dev, "PCI core found at %pR\n", res);
+
+ /* Set the BCR's to enable PCI access */
+ word = __raw_readl(bsc + BSC_BCR1);
+ word |= BSC_BCR1_BRQEN;
+ __raw_writel(word, bsc + BSC_BCR1);
+
+ /* Turn the clocks back on (not done in reset)*/
+ pcic_writel(PCIC_WE_KEY | 0, pcic, PCIC_PCICLKR);
+ /* Clear Powerdown IRQ's (not done in reset) */
+ word = PCIC_PCIPINT_D3 | PCIC_PCIPINT_D0;
+ pcic_writel(word, pcic, PCIC_PCIPINT);
+
+ /* set the command/status */
+ word = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_PARITY | PCI_COMMAND_WAIT;
+ pcic_writel(word, pcic, PCI_COMMAND);
+
+ /* define this host as the host bridge */
+ word = PCI_BASE_CLASS_BRIDGE << 24;
+ pcic_writel(word, pcic, PCI_CLASS_REVISION);
+
+ ret = pci_host_common_probe(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Initialize failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* Set IO and Mem windows to local address */
+ if (set_pci_ranges(&pdev->dev, pcic, bsc,
+ did == PCI_DEVICE_ID_SH7751R))
+ return -EINVAL;
+ pcic_writel(0, pcic, PCIC_PCIIOBR);
+
+ if (of_property_read_bool(pdev->dev.of_node, "renesas,bus-arbit-round-robin"))
+ word |= BIT(0);
+ else
+ word = 0;
+ pcic_writel(word, pcic, PCIC_PCIDMABT);
+
+ /* SH7751 init done, set central function init complete */
+ /* use round robin mode to stop a device starving/overrunning */
+ word = PCIC_PCICR_CFIN | PCIC_PCICR_ARBM;
+ pcic_writel(PCIC_WE_KEY | word, pcic, PCIC_PCICR);
+
+ return 0;
+}
+
+/*
+ * Direct access to PCI hardware...
+ */
+#define CONFIG_CMD(bus, devfn, where) \
+ (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
+
+static void __iomem *sh4_pci_map_bus(struct pci_bus *bus,
+ unsigned int devfn, int where)
+{
+ struct pci_config_window *cfg = bus->sysdata;
+ void __iomem *pcic = (void __iomem *)cfg->res.start;
+
+ pcic_writel(CONFIG_CMD(bus, devfn, where), pcic, PCIC_PCIPAR);
+ return pcic + PCIC_PCIPDR;
+}
+
+static const struct pci_ecam_ops pci_sh7751_bus_ops = {
+ .pci_ops = {
+ .map_bus = sh4_pci_map_bus,
+ .read = pci_generic_config_read32,
+ .write = pci_generic_config_write32,
+ }
+};
+
+static const struct of_device_id sh7751_pci_of_match[] = {
+ { .compatible = "renesas,sh7751-pci",
+ .data = &pci_sh7751_bus_ops },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh7751_pci_of_match);
+
+static struct platform_driver sh7751_pci_driver = {
+ .driver = {
+ .name = "sh7751-pci",
+ .of_match_table = sh7751_pci_of_match,
+ },
+ .probe = sh7751_pci_probe,
+};
+module_platform_driver(sh7751_pci_driver);
+
+MODULE_DESCRIPTION("SH7751 PCI driver");
--
2.39.2


2024-04-04 05:02:08

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 09/37] dt-binding: Add compatible SH7750 SoC

Signed-off-by: Yoshinori Sato <[email protected]>
---
Documentation/devicetree/bindings/timer/renesas,tmu.yaml | 2 ++
1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/timer/renesas,tmu.yaml b/Documentation/devicetree/bindings/timer/renesas,tmu.yaml
index 84bbe15028a1..265d286ffb2f 100644
--- a/Documentation/devicetree/bindings/timer/renesas,tmu.yaml
+++ b/Documentation/devicetree/bindings/timer/renesas,tmu.yaml
@@ -39,6 +39,7 @@ properties:
- renesas,tmu-r8a779a0 # R-Car V3U
- renesas,tmu-r8a779f0 # R-Car S4-8
- renesas,tmu-r8a779g0 # R-Car V4H
+ - renesas,tmu-sh7750 # SH7750
- const: renesas,tmu

reg:
@@ -96,6 +97,7 @@ if:
- renesas,tmu-r8a7740
- renesas,tmu-r8a7778
- renesas,tmu-r8a7779
+ - renesas,tmu-sh7750
then:
required:
- resets
--
2.39.2


2024-04-04 05:01:26

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 07/37] sh: Fix COMMON_CLK support in CONFIG_OF=y.

Initialize the clock and timer using the COMMON_CLK procedure.
sh's earlytimer mechanism doesn't work properly in OF,
so timer initialization is delayed.
If CONFIG_OF=y, perform the general timer initialization procedure.

Signed-off-by: Yoshinori Sato <[email protected]>
---
arch/sh/boards/of-generic.c | 28 ++++------------------------
arch/sh/kernel/time.c | 12 ++++++++++++
2 files changed, 16 insertions(+), 24 deletions(-)

diff --git a/arch/sh/boards/of-generic.c b/arch/sh/boards/of-generic.c
index f7f3e618e85b..f1ca5a914c11 100644
--- a/arch/sh/boards/of-generic.c
+++ b/arch/sh/boards/of-generic.c
@@ -8,6 +8,7 @@
#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
#include <linux/clocksource.h>
#include <linux/irqchip.h>
#include <asm/machvec.h>
@@ -98,16 +99,7 @@ static void sh_of_smp_probe(void)

#endif

-static void noop(void)
-{
-}
-
-static int noopi(void)
-{
- return 0;
-}
-
-static void __init sh_of_mem_reserve(void)
+static void __init sh_of_mem_init(void)
{
early_init_fdt_reserve_self();
early_init_fdt_scan_reserved_mem();
@@ -140,25 +132,13 @@ static void __init sh_of_init_irq(void)
irqchip_init();
}

-static int __init sh_of_clk_init(void)
-{
-#ifdef CONFIG_COMMON_CLK
- /* Disabled pending move to COMMON_CLK framework. */
- pr_info("SH generic board support: scanning for clk providers\n");
- of_clk_init(NULL);
-#endif
- return 0;
-}
-
static struct sh_machine_vector __initmv sh_of_generic_mv = {
.mv_setup = sh_of_setup,
.mv_name = "devicetree", /* replaced by DT root's model */
.mv_irq_demux = sh_of_irq_demux,
.mv_init_irq = sh_of_init_irq,
- .mv_clk_init = sh_of_clk_init,
- .mv_mode_pins = noopi,
- .mv_mem_init = noop,
- .mv_mem_reserve = sh_of_mem_reserve,
+ .mv_mode_pins = generic_mode_pins,
+ .mv_mem_init = sh_of_mem_init,
};

struct sh_clk_ops;
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c
index 821a09cbd605..ce5b7c2f8628 100644
--- a/arch/sh/kernel/time.c
+++ b/arch/sh/kernel/time.c
@@ -19,7 +19,9 @@
#include <asm/clock.h>
#include <asm/rtc.h>
#include <asm/platform_early.h>
+#include <linux/of_clk.h>

+#ifndef CONFIG_SH_DEVICE_TREE
static void __init sh_late_time_init(void)
{
/*
@@ -43,3 +45,13 @@ void __init time_init(void)

late_time_init = sh_late_time_init;
}
+#else
+/* CONFIG_SH_DEVICE_TREE */
+void __init time_init(void)
+{
+ pr_info("SH generic board support: scanning for clk providers\n");
+
+ of_clk_init(NULL);
+ timer_probe();
+}
+#endif
--
2.39.2


2024-04-04 05:01:17

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 06/37] sh: kernel/setup Update DT support.

Fix extrnal fdt initialize and bootargs.

Signed-off-by: Yoshinori Sato <[email protected]>
---
arch/sh/Kconfig | 23 +++++++++++------------
arch/sh/include/asm/setup.h | 1 +
arch/sh/kernel/setup.c | 36 +++++++++++++++++++++++-------------
3 files changed, 35 insertions(+), 25 deletions(-)

diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 6711cde0d973..242cf30e704d 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -708,17 +708,22 @@ config ROMIMAGE_MMCIF
first part of the romImage which in turn loads the rest the kernel
image to RAM using the MMCIF hardware block.

+config CMDLINE
+ string "Kernel command line arguments string"
+ default "console=ttySC1,115200"
+
choice
prompt "Kernel command line"
- optional
- default CMDLINE_OVERWRITE
- depends on !OF || USE_BUILTIN_DTB
+ default CMDLINE_BOOTLOADER
+
+config CMDLINE_BOOTLOADER
+ bool "Use bootloader kernel arguments"
help
- Setting this option allows the kernel command line arguments
- to be set.
+ Uses the command-line options passed by the boot loader.
+ If boot loader dosen't provide kernel argments, Use built-in argments.

config CMDLINE_OVERWRITE
- bool "Overwrite bootloader kernel arguments"
+ bool "Overwrite built-in kernel arguments"
help
Given string will overwrite any arguments passed in by
a bootloader.
@@ -730,12 +735,6 @@ config CMDLINE_EXTEND
by a bootloader.

endchoice
-
-config CMDLINE
- string "Kernel command line arguments string"
- depends on CMDLINE_OVERWRITE || CMDLINE_EXTEND
- default "console=ttySC1,115200"
-
endmenu

menu "Bus options"
diff --git a/arch/sh/include/asm/setup.h b/arch/sh/include/asm/setup.h
index fc807011187f..84bb23a771f3 100644
--- a/arch/sh/include/asm/setup.h
+++ b/arch/sh/include/asm/setup.h
@@ -21,5 +21,6 @@
void sh_mv_setup(void);
void check_for_initrd(void);
void per_cpu_trap_init(void);
+void sh_fdt_init(phys_addr_t dt_phys);

#endif /* _SH_SETUP_H */
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index 620e5cf8ae1e..42e6292a40cf 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -30,6 +30,7 @@
#include <linux/memblock.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
#include <linux/uaccess.h>
#include <uapi/linux/mount.h>
#include <asm/io.h>
@@ -269,8 +270,22 @@ void __ref sh_fdt_init(phys_addr_t dt_phys)

void __init setup_arch(char **cmdline_p)
{
+#if defined(CONFIG_OF) && defined(CONFIG_OF_EARLY_FLATTREE)
+ if (IS_ENABLED(CONFIG_USE_BUILTIN_DTB)) {
+ /* Relocate Embedded DTB */
+ unflatten_and_copy_device_tree();
+ } else if (initial_boot_params) {
+ /* Reserve external DTB area */
+ memblock_reserve(__pa(initial_boot_params),
+ fdt_totalsize(initial_boot_params));
+ unflatten_device_tree();
+ }
+ /* copy from /chosen/bootargs */
+ strscpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
+#endif
enable_mmu();

+#ifndef CONFIG_OF
ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV);

printk(KERN_NOTICE "Boot params:\n"
@@ -299,14 +314,17 @@ void __init setup_arch(char **cmdline_p)
bss_resource.start = virt_to_phys(__bss_start);
bss_resource.end = virt_to_phys(__bss_stop)-1;

-#ifdef CONFIG_CMDLINE_OVERWRITE
- strscpy(command_line, CONFIG_CMDLINE, sizeof(command_line));
-#else
- strscpy(command_line, COMMAND_LINE, sizeof(command_line));
+#endif
+#if !defined(CONFIG_OF) || defined(CONFIG_USE_BUILTIN_DTB)
+ if (*COMMAND_LINE)
+ strscpy(command_line, COMMAND_LINE, sizeof(command_line));
+#endif
+ if (*command_line == '\0' || IS_ENABLED(CONFIG_CMDLINE_OVERWRITE))
+ /* Use built-in parameter */
+ strscpy(command_line, CONFIG_CMDLINE, sizeof(command_line));
#ifdef CONFIG_CMDLINE_EXTEND
strlcat(command_line, " ", sizeof(command_line));
strlcat(command_line, CONFIG_CMDLINE, sizeof(command_line));
-#endif
#endif

/* Save unparsed command line copy for /proc/cmdline */
@@ -322,14 +340,6 @@ void __init setup_arch(char **cmdline_p)
/* Let earlyprintk output early console messages */
sh_early_platform_driver_probe("earlyprintk", 1, 1);

-#ifdef CONFIG_OF_EARLY_FLATTREE
-#ifdef CONFIG_USE_BUILTIN_DTB
- unflatten_and_copy_device_tree();
-#else
- unflatten_device_tree();
-#endif
-#endif
-
paging_init();

/* Perform the machine specific initialisation */
--
2.39.2


2024-04-04 05:01:53

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 08/37] clocksource: sh_tmu: CLOCKSOURCE support.

Allows initialization as CLOCKSOURCE.

Signed-off-by: Yoshinori Sato <[email protected]>
---
drivers/clocksource/sh_tmu.c | 198 ++++++++++++++++++++++++-----------
1 file changed, 134 insertions(+), 64 deletions(-)

diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index beffff81c00f..59f9da7fd987 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -17,6 +17,8 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -51,6 +53,7 @@ struct sh_tmu_channel {

struct sh_tmu_device {
struct platform_device *pdev;
+ struct device_node *np;

void __iomem *mapbase;
struct clk *clk;
@@ -65,6 +68,7 @@ struct sh_tmu_device {

bool has_clockevent;
bool has_clocksource;
+ const char *name;
};

#define TSTR -1 /* shared register */
@@ -148,8 +152,8 @@ static int __sh_tmu_enable(struct sh_tmu_channel *ch)
/* enable clock */
ret = clk_enable(ch->tmu->clk);
if (ret) {
- dev_err(&ch->tmu->pdev->dev, "ch%u: cannot enable clock\n",
- ch->index);
+ pr_err("%s ch%u: cannot enable clock\n",
+ ch->tmu->name, ch->index);
return ret;
}

@@ -174,9 +178,10 @@ static int sh_tmu_enable(struct sh_tmu_channel *ch)
if (ch->enable_count++ > 0)
return 0;

- pm_runtime_get_sync(&ch->tmu->pdev->dev);
- dev_pm_syscore_device(&ch->tmu->pdev->dev, true);
-
+ if (ch->tmu->pdev) {
+ pm_runtime_get_sync(&ch->tmu->pdev->dev);
+ dev_pm_syscore_device(&ch->tmu->pdev->dev, true);
+ }
return __sh_tmu_enable(ch);
}

@@ -202,8 +207,10 @@ static void sh_tmu_disable(struct sh_tmu_channel *ch)

__sh_tmu_disable(ch);

- dev_pm_syscore_device(&ch->tmu->pdev->dev, false);
- pm_runtime_put(&ch->tmu->pdev->dev);
+ if (ch->tmu->pdev) {
+ dev_pm_syscore_device(&ch->tmu->pdev->dev, false);
+ pm_runtime_put(&ch->tmu->pdev->dev);
+ }
}

static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta,
@@ -245,7 +252,7 @@ static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}

-static struct sh_tmu_channel *cs_to_sh_tmu(struct clocksource *cs)
+static inline struct sh_tmu_channel *cs_to_sh_tmu(struct clocksource *cs)
{
return container_of(cs, struct sh_tmu_channel, cs);
}
@@ -292,7 +299,8 @@ static void sh_tmu_clocksource_suspend(struct clocksource *cs)

if (--ch->enable_count == 0) {
__sh_tmu_disable(ch);
- dev_pm_genpd_suspend(&ch->tmu->pdev->dev);
+ if (ch->tmu->pdev)
+ dev_pm_genpd_suspend(&ch->tmu->pdev->dev);
}
}

@@ -304,7 +312,8 @@ static void sh_tmu_clocksource_resume(struct clocksource *cs)
return;

if (ch->enable_count++ == 0) {
- dev_pm_genpd_resume(&ch->tmu->pdev->dev);
+ if (ch->tmu->pdev)
+ dev_pm_genpd_resume(&ch->tmu->pdev->dev);
__sh_tmu_enable(ch);
}
}
@@ -324,14 +333,14 @@ static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch,
cs->mask = CLOCKSOURCE_MASK(32);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;

- dev_info(&ch->tmu->pdev->dev, "ch%u: used as clock source\n",
- ch->index);
+ pr_info("%s ch%u: used as clock source\n",
+ ch->tmu->name, ch->index);

clocksource_register_hz(cs, ch->tmu->rate);
return 0;
}

-static struct sh_tmu_channel *ced_to_sh_tmu(struct clock_event_device *ced)
+static inline struct sh_tmu_channel *ced_to_sh_tmu(struct clock_event_device *ced)
{
return container_of(ced, struct sh_tmu_channel, ced);
}
@@ -364,8 +373,8 @@ static int sh_tmu_clock_event_set_state(struct clock_event_device *ced,
if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
sh_tmu_disable(ch);

- dev_info(&ch->tmu->pdev->dev, "ch%u: used for %s clock events\n",
- ch->index, periodic ? "periodic" : "oneshot");
+ pr_info("%s ch%u: used for %s clock events\n",
+ ch->tmu->name, ch->index, periodic ? "periodic" : "oneshot");
sh_tmu_clock_event_start(ch, periodic);
return 0;
}
@@ -417,20 +426,22 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
ced->set_state_shutdown = sh_tmu_clock_event_shutdown;
ced->set_state_periodic = sh_tmu_clock_event_set_periodic;
ced->set_state_oneshot = sh_tmu_clock_event_set_oneshot;
- ced->suspend = sh_tmu_clock_event_suspend;
- ced->resume = sh_tmu_clock_event_resume;
+ if (ch->tmu->pdev) {
+ ced->suspend = sh_tmu_clock_event_suspend;
+ ced->resume = sh_tmu_clock_event_resume;
+ }

- dev_info(&ch->tmu->pdev->dev, "ch%u: used for clock events\n",
- ch->index);
+ pr_info("%s ch%u: used for clock events\n",
+ ch->tmu->name, ch->index);

clockevents_config_and_register(ced, ch->tmu->rate, 0x300, 0xffffffff);

ret = request_irq(ch->irq, sh_tmu_interrupt,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
- dev_name(&ch->tmu->pdev->dev), ch);
+ ch->tmu->name, ch);
if (ret) {
- dev_err(&ch->tmu->pdev->dev, "ch%u: failed to request irq %d\n",
- ch->index, ch->irq);
+ pr_err("%s ch%u: failed to request irq %d\n",
+ ch->tmu->name, ch->index, ch->irq);
return;
}
}
@@ -465,28 +476,36 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
else
ch->base = tmu->mapbase + 8 + ch->index * 12;

- ch->irq = platform_get_irq(tmu->pdev, index);
+ if (tmu->pdev)
+ ch->irq = platform_get_irq(tmu->pdev, index);
+ if (tmu->np)
+ ch->irq = of_irq_get(tmu->np, index);
+
if (ch->irq < 0)
return ch->irq;

ch->cs_enabled = false;
ch->enable_count = 0;

- return sh_tmu_register(ch, dev_name(&tmu->pdev->dev),
- clockevent, clocksource);
+ return sh_tmu_register(ch, tmu->name, clockevent, clocksource);
}

static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
{
struct resource *res;

- res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&tmu->pdev->dev, "failed to get I/O memory\n");
- return -ENXIO;
+ if (tmu->pdev) {
+ res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_err("sh_tmu failed to get I/O memory\n");
+ return -ENXIO;
+ }
+
+ tmu->mapbase = ioremap(res->start, resource_size(res));
}
+ if (tmu->np)
+ tmu->mapbase = of_iomap(tmu->np, 0);

- tmu->mapbase = ioremap(res->start, resource_size(res));
if (tmu->mapbase == NULL)
return -ENXIO;

@@ -495,7 +514,12 @@ static int sh_tmu_map_memory(struct sh_tmu_device *tmu)

static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
{
- struct device_node *np = tmu->pdev->dev.of_node;
+ struct device_node *np;
+
+ if (tmu->pdev)
+ np = tmu->pdev->dev.of_node;
+ if (tmu->np)
+ np = tmu->np;

tmu->model = SH_TMU;
tmu->num_channels = 3;
@@ -503,45 +527,19 @@ static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
of_property_read_u32(np, "#renesas,channels", &tmu->num_channels);

if (tmu->num_channels != 2 && tmu->num_channels != 3) {
- dev_err(&tmu->pdev->dev, "invalid number of channels %u\n",
- tmu->num_channels);
+ pr_err("%s: invalid number of channels %u\n",
+ tmu->name, tmu->num_channels);
return -EINVAL;
}

return 0;
}

-static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
+static int sh_tmu_setup(struct sh_tmu_device *tmu)
{
unsigned int i;
int ret;

- tmu->pdev = pdev;
-
- raw_spin_lock_init(&tmu->lock);
-
- if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
- ret = sh_tmu_parse_dt(tmu);
- if (ret < 0)
- return ret;
- } else if (pdev->dev.platform_data) {
- const struct platform_device_id *id = pdev->id_entry;
- struct sh_timer_config *cfg = pdev->dev.platform_data;
-
- tmu->model = id->driver_data;
- tmu->num_channels = hweight8(cfg->channels_mask);
- } else {
- dev_err(&tmu->pdev->dev, "missing platform data\n");
- return -ENXIO;
- }
-
- /* Get hold of clock. */
- tmu->clk = clk_get(&tmu->pdev->dev, "fck");
- if (IS_ERR(tmu->clk)) {
- dev_err(&tmu->pdev->dev, "cannot get clock\n");
- return PTR_ERR(tmu->clk);
- }
-
ret = clk_prepare(tmu->clk);
if (ret < 0)
goto err_clk_put;
@@ -557,7 +555,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
/* Map the memory resource. */
ret = sh_tmu_map_memory(tmu);
if (ret < 0) {
- dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n");
+ pr_err("%s: failed to remap I/O memory\n", tmu->name);
goto err_clk_unprepare;
}

@@ -580,8 +578,6 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
goto err_unmap;
}

- platform_set_drvdata(pdev, tmu);
-
return 0;

err_unmap:
@@ -594,6 +590,39 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
return ret;
}

+static int sh_tmu_setup_pdev(struct sh_tmu_device *tmu, struct platform_device *pdev)
+{
+ int ret;
+
+ tmu->pdev = pdev;
+
+ raw_spin_lock_init(&tmu->lock);
+
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ ret = sh_tmu_parse_dt(tmu);
+ if (ret < 0)
+ return ret;
+ } else if (pdev->dev.platform_data) {
+ const struct platform_device_id *id = pdev->id_entry;
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+
+ tmu->model = id->driver_data;
+ tmu->num_channels = hweight8(cfg->channels_mask);
+ } else {
+ dev_err(&tmu->pdev->dev, "missing platform data\n");
+ return -ENXIO;
+ }
+
+ tmu->name = dev_name(&pdev->dev);
+ tmu->clk = clk_get(&tmu->pdev->dev, "fck");
+ if (IS_ERR(tmu->clk)) {
+ dev_err(&tmu->pdev->dev, "cannot get clock\n");
+ return PTR_ERR(tmu->clk);
+ }
+
+ return sh_tmu_setup(tmu);
+}
+
static int sh_tmu_probe(struct platform_device *pdev)
{
struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
@@ -613,12 +642,13 @@ static int sh_tmu_probe(struct platform_device *pdev)
if (tmu == NULL)
return -ENOMEM;

- ret = sh_tmu_setup(tmu, pdev);
+ ret = sh_tmu_setup_pdev(tmu, pdev);
if (ret) {
kfree(tmu);
pm_runtime_idle(&pdev->dev);
return ret;
}
+ platform_set_drvdata(pdev, tmu);

if (is_sh_early_platform_device(pdev))
return 0;
@@ -632,6 +662,45 @@ static int sh_tmu_probe(struct platform_device *pdev)
return 0;
}

+static int sh_tmu_setup_of(struct sh_tmu_device *tmu, struct device_node *np)
+{
+ int ret;
+
+ tmu->np = np;
+ raw_spin_lock_init(&tmu->lock);
+
+ ret = sh_tmu_parse_dt(tmu);
+ if (ret < 0)
+ return ret;
+
+ tmu->clk = of_clk_get(np, 0);
+ tmu->name = of_node_full_name(np);
+
+ if (IS_ERR(tmu->clk)) {
+ pr_err("%pOF: cannot get clock\n", np);
+ return PTR_ERR(tmu->clk);
+ }
+
+ return sh_tmu_setup(tmu);
+}
+
+static int __init sh_tmu_of_register(struct device_node *np)
+{
+ struct sh_tmu_device *tmu;
+ int ret;
+
+ tmu = kzalloc(sizeof(*tmu), GFP_KERNEL);
+ if (tmu == NULL)
+ return -ENOMEM;
+
+ ret = sh_tmu_setup_of(tmu, np);
+ if (ret) {
+ kfree(tmu);
+ pr_warn("%pOF: Timer register failed (%d)", np, ret);
+ }
+ return ret;
+}
+
static const struct platform_device_id sh_tmu_id_table[] = {
{ "sh-tmu", SH_TMU },
{ "sh-tmu-sh3", SH_TMU_SH3 },
@@ -665,6 +734,7 @@ static void __exit sh_tmu_exit(void)
platform_driver_unregister(&sh_tmu_device_driver);
}

+TIMER_OF_DECLARE(sh_tmu, "renesas,tmu", sh_tmu_of_register);
#ifdef CONFIG_SUPERH
sh_early_platform_init("earlytimer", &sh_tmu_device_driver);
#endif
--
2.39.2


2024-04-04 05:01:00

by Yoshinori Sato

[permalink] [raw]
Subject: [PATCH v7 05/37] sh: GENERIC_IRQ_CHIP support for CONFIG_OF=y

Remove unused function prototype.
Add helper update_sr_imask. use for SH7751 irq driver.
Add stub intc_finalize.

Signed-off-by: Yoshinori Sato <[email protected]>
---
arch/sh/include/asm/io.h | 2 ++
arch/sh/include/asm/irq.h | 10 ++++++++--
arch/sh/kernel/cpu/Makefile | 5 +----
arch/sh/kernel/cpu/irq/imask.c | 17 +++++++++++++++++
include/linux/sh_intc.h | 7 ++++++-
5 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h
index cf5eab840d57..5c544cf5201b 100644
--- a/arch/sh/include/asm/io.h
+++ b/arch/sh/include/asm/io.h
@@ -121,7 +121,9 @@ __BUILD_MEMORY_STRING(__raw_, q, u64)

#define ioport_map ioport_map
#define ioport_unmap ioport_unmap
+#ifndef CONFIG_SH_DEVICE_TREE
#define pci_iounmap pci_iounmap
+#endif

#define ioread8 ioread8
#define ioread16 ioread16
diff --git a/arch/sh/include/asm/irq.h b/arch/sh/include/asm/irq.h
index 0f384b1f45ca..3d897229dcc4 100644
--- a/arch/sh/include/asm/irq.h
+++ b/arch/sh/include/asm/irq.h
@@ -16,8 +16,8 @@
/*
* Simple Mask Register Support
*/
-extern void make_maskreg_irq(unsigned int irq);
-extern unsigned short *irq_mask_register;
+
+void update_sr_imask(unsigned int irq, bool enable);

/*
* PINT IRQs
@@ -54,4 +54,10 @@ extern void irq_finish(unsigned int irq);

#include <asm-generic/irq.h>

+/* SH3/4 INTC stuff */
+/* IRL level 0 - 15 */
+#define NR_IRL 15
+/* IRL0 -> IRQ16 */
+#define IRL_BASE_IRQ 16
+
#endif /* __ASM_SH_IRQ_H */
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index e00ebf134985..ad12807fae9c 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -20,7 +20,4 @@ ifndef CONFIG_COMMON_CLK
obj-y += clock.o
obj-$(CONFIG_SH_CLK_CPG_LEGACY) += clock-cpg.o
endif
-ifndef CONFIG_GENERIC_IRQ_CHIP
-obj-y += irq/
-endif
-obj-y += init.o fpu.o pfc.o proc.o
+obj-y += init.o fpu.o pfc.o proc.o irq/
diff --git a/arch/sh/kernel/cpu/irq/imask.c b/arch/sh/kernel/cpu/irq/imask.c
index 572585c3f2fd..7589ca7c506c 100644
--- a/arch/sh/kernel/cpu/irq/imask.c
+++ b/arch/sh/kernel/cpu/irq/imask.c
@@ -51,6 +51,7 @@ static inline void set_interrupt_registers(int ip)
: "t");
}

+#ifndef CONFIG_GENERIC_IRQ_CHIP
static void mask_imask_irq(struct irq_data *data)
{
unsigned int irq = data->irq;
@@ -83,3 +84,19 @@ void make_imask_irq(unsigned int irq)
irq_set_chip_and_handler_name(irq, &imask_irq_chip, handle_level_irq,
"level");
}
+#else
+void update_sr_imask(unsigned int irq, bool enable)
+{
+ if (enable) {
+ set_bit(irq, imask_mask);
+ interrupt_priority = IMASK_PRIORITY -
+ find_first_bit(imask_mask, IMASK_PRIORITY);
+ } else {
+ clear_bit(irq, imask_mask);
+ if (interrupt_priority < IMASK_PRIORITY - irq)
+ interrupt_priority = IMASK_PRIORITY - irq;
+ }
+ set_interrupt_registers(interrupt_priority);
+}
+EXPORT_SYMBOL(update_sr_imask);
+#endif
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h
index 27ae79191bdc..994b5b05a0d7 100644
--- a/include/linux/sh_intc.h
+++ b/include/linux/sh_intc.h
@@ -139,8 +139,13 @@ struct intc_desc symbol __initdata = { \
int register_intc_controller(struct intc_desc *desc);
int intc_set_priority(unsigned int irq, unsigned int prio);
int intc_irq_lookup(const char *chipname, intc_enum enum_id);
+#ifndef CONFIG_SH_DEVICE_TREE
void intc_finalize(void);
-
+#else
+static inline void intc_finalize(void)
+{
+}
+#endif
#ifdef CONFIG_INTC_USERIMASK
int register_intc_userimask(unsigned long addr);
#else
--
2.39.2


2024-04-08 21:34:57

by Mayank Rana

[permalink] [raw]
Subject: Re: [PATCH v7 11/37] pci: pci-sh7751: Add SH7751 PCI driver

Hi,

On 4/3/2024 9:59 PM, Yoshinori Sato wrote:
> Renesas SH7751 CPU Internal PCI Controller driver.
>
> Signed-off-by: Yoshinori Sato <[email protected]>
> ---
> drivers/pci/controller/Kconfig | 9 +
> drivers/pci/controller/Makefile | 1 +
> drivers/pci/controller/pci-sh7751.c | 342 ++++++++++++++++++++++++++++
> 3 files changed, 352 insertions(+)
> create mode 100644 drivers/pci/controller/pci-sh7751.c
>
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index e534c02ee34f..a2fd917a2e03 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -353,6 +353,15 @@ config PCIE_XILINX_CPM
> Say 'Y' here if you want kernel support for the
> Xilinx Versal CPM host bridge.
>
> +config PCI_SH7751
> + bool "Renesas SH7751 PCI controller"
> + depends on OF
> + depends on CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7751R || COMPILE_TEST
> + select PCI_HOST_COMMON
> + help
> + Say 'Y' here if you want kernel to support the Renesas SH7751 PCI
> + Host Bridge driver.
> +
> source "drivers/pci/controller/cadence/Kconfig"
> source "drivers/pci/controller/dwc/Kconfig"
> source "drivers/pci/controller/mobiveil/Kconfig"
> diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
> index f2b19e6174af..aa97e5d74e58 100644
> --- a/drivers/pci/controller/Makefile
> +++ b/drivers/pci/controller/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
> obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o
> obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o
> obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o
> +obj-$(CONFIG_PCI_SH7751) += pci-sh7751.o
>
> # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
> obj-y += dwc/
> diff --git a/drivers/pci/controller/pci-sh7751.c b/drivers/pci/controller/pci-sh7751.c
> new file mode 100644
> index 000000000000..a5340689f737
> --- /dev/null
> +++ b/drivers/pci/controller/pci-sh7751.c
> @@ -0,0 +1,342 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SH7751 PCI driver
> + * Copyright (C) 2023 Yoshinori Sato
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci-ecam.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/pci.h>
> +#include <linux/dma-direct.h>
> +#include <asm/addrspace.h>
can you consider to rearranging headers into alphabetically sorted order ?
> +
> +/* PCICR and PCICLKCR write enable magic key */
> +#define PCIC_WE_KEY (0xa5 << 24)
> +
> +/* PCIC registers */
> +/* 0x0000 - 0x00ff mapped to PCI device configuration space */
> +#define PCIC_PCICR 0x100 /* PCI Control Register */
> +#define PCIC_PCICR_TRSB BIT(9) /* Target Read Single */
> +#define PCIC_PCICR_BSWP BIT(8) /* Target Byte Swap */
> +#define PCIC_PCICR_PLUP BIT(7) /* Enable PCI Pullup */
> +#define PCIC_PCICR_ARBM BIT(6) /* PCI Arbitration Mode */
> +#define PCIC_PCICR_MD10 BIT(5) /* MD10 status */
> +#define PCIC_PCICR_MD9 BIT(4) /* MD9 status */
> +#define PCIC_PCICR_SERR BIT(3) /* SERR output assert */
> +#define PCIC_PCICR_INTA BIT(2) /* INTA output assert */
> +#define PCIC_PCICR_PRST BIT(1) /* PCI Reset Assert */
> +#define PCIC_PCICR_CFIN BIT(0) /* Central Fun. Init Done */
> +
> +#define PCIC_PCILSR0 0x104 /* PCI Local Space Register0 */
> +#define PCIC_PCILSR1 0x108 /* PCI Local Space Register1 */
> +#define PCIC_PCILAR0 0x10c /* PCI Local Addr Register1 */
> +#define PCIC_PCILAR1 0x110 /* PCI Local Addr Register1 */
> +#define PCIC_PCIINT 0x114 /* PCI Interrupt Register */
> +#define PCIC_PCIINTM 0x118 /* PCI Interrupt Mask */
> +#define PCIC_PCIALR 0x11c /* Error Address Register */
> +#define PCIC_PCICLR 0x120 /* Error Command/Data */
> +#define PCIC_PCIAINT 0x130 /* Arbiter Interrupt Register */
> +#define PCIC_PCIAINTM 0x134 /* Arbiter Int. Mask Register */
> +#define PCIC_PCIBMLR 0x138 /* Error Bus Master Register */
> +#define PCIC_PCIDMABT 0x140 /* DMA Transfer Arb. Register */
> +#define PCIC_PCIPAR 0x1c0 /* PIO Address Register */
> +#define PCIC_PCIMBR 0x1c4 /* Memory Base Address */
> +#define PCIC_PCIIOBR 0x1c8 /* I/O Base Address Register */
> +
> +#define PCIC_PCIPINT 0x1cc /* Power Mgmnt Int. Register */
> +#define PCIC_PCIPINT_D3 BIT(1) /* D3 Pwr Mgmt. Interrupt */
> +#define PCIC_PCIPINT_D0 BIT(0) /* D0 Pwr Mgmt. Interrupt */
> +
> +#define PCIC_PCIPINTM 0x1d0 /* Power Mgmnt Mask Register */
> +#define PCIC_PCICLKR 0x1d4 /* Clock Ctrl. Register */
> +#define PCIC_PCIBCR1 0x1e0 /* Memory BCR1 Register */
> +#define PCIC_PCIBCR2 0x1e4 /* Memory BCR2 Register */
> +#define PCIC_PCIWCR1 0x1e8 /* Wait Control 1 Register */
> +#define PCIC_PCIWCR2 0x1ec /* Wait Control 2 Register */
> +#define PCIC_PCIWCR3 0x1f0 /* Wait Control 3 Register */
> +#define PCIC_PCIMCR 0x1f4 /* Memory Control Register */
> +#define PCIC_PCIBCR3 0x1f8 /* Memory BCR3 Register */
> +#define PCIC_PCIPDR 0x220 /* Port IO Data Register */
> +
> +/* PCI IDs */
> +/* Hitachi is the company that led to Renesas. */
> +/* The SH7751 was designed by Hitachi, so it has a Hitachi ID. */
multi-line comments way ?
> +#define PCI_VENDOR_ID_HITACHI 0x1054
> +#define PCI_DEVICE_ID_SH7751 0x3505
> +#define PCI_DEVICE_ID_SH7751R 0x350e
> +
> +/* BSC registers */
> +/* Copy BSC setting to PCI BSC */
> +#define BSC_BCR1 0x0000
> +#define BSC_BCR1_SLAVE BIT(30)
> +#define BSC_BCR1_BRQEN BIT(19)
> +#define BSC_BCR2 0x0004
> +#define BSC_BCR3 0x0050
> +#define BSC_WCR1 0x0008
> +#define BSC_WCR2 0x000c
> +#define BSC_WCR3 0x0010
> +#define BSC_MCR 0x0014
> +#define BSC_MCR_MRSET BIT(30)
> +#define BSC_MCR_RFSH BIT(2)
> +
> +/* PCIC access wrapper */
> +#define pcic_writel(val, base, reg) writel(val, base + (reg))
> +#define pcic_readl(base, reg) readl(base + (reg))
Do you really needed these new macros ? Is it for better readability ?
> +/*
> + * We need to avoid collisions with `mirrored' VGA ports
> + * and other strange ISA hardware, so we always want the
> + * addresses to be allocated in the 0x000-0x0ff region
> + * modulo 0x400.
> + */
> +#define IO_REGION_BASE 0x1000
> +resource_size_t pcibios_align_resource(void *data, const struct resource *res,
> + resource_size_t size, resource_size_t align)
> +{
> + resource_size_t start = res->start;
> +
> + if (res->flags & IORESOURCE_IO) {
> + if (start < PCIBIOS_MIN_IO + IO_REGION_BASE)
> + start = PCIBIOS_MIN_IO + IO_REGION_BASE;
> +
> + /*
> + * Put everything into 0x00-0xff region modulo 0x400.
> + */
single line comment would work. no ?
> + if (start & 0x300)
> + start = (start + 0x3ff) & ~0x3ff;
> + }
> +
> + return start;
> +}
> +
> +static int setup_pci_bsc(struct device *dev, void __iomem *pcic,
> + void __iomem *bsc, unsigned int area, bool bcr3)
> +{
> + u32 word;
> +
> + word = __raw_readl(bsc + BSC_BCR1);
> + /* check BCR for SDRAM in area */
> + if (((word >> area) & 1) == 0) {
> + dev_err(dev, "Area %u is not configured for SDRAM. BCR1=0x%x\n",
> + area, word);
> + return -EINVAL;
> + }
> + word |= BSC_BCR1_SLAVE; /* PCIC BSC is slave only */
> + pcic_writel(word, pcic, PCIC_PCIBCR1);
> +
> + word = __raw_readw(bsc + BSC_BCR2);
> + /* check BCR2 for 32bit SDRAM interface*/
> + if (((word >> (area << 1)) & 0x3) != 0x3) {
> + dev_err(dev, "Area %u is not 32 bit SDRAM. BCR2=0x%x\n",
> + area, word);
> + return -EINVAL;
> + }
> + pcic_writel(word, pcic, PCIC_PCIBCR2);
> +
> + if (bcr3) {
> + /* BCR3 have only SH7751R */
> + word = __raw_readw(bsc + BSC_BCR3);
> + pcic_writel(word, pcic, PCIC_PCIBCR3);
> + }
> +
> + /* configure the wait control registers */
> + word = __raw_readl(bsc + BSC_WCR1);
> + pcic_writel(word, pcic, PCIC_PCIWCR1);
> + word = __raw_readl(bsc + BSC_WCR2);
> + pcic_writel(word, pcic, PCIC_PCIWCR2);
> + word = __raw_readl(bsc + BSC_WCR3);
> + pcic_writel(word, pcic, PCIC_PCIWCR3);
> + word = __raw_readl(bsc + BSC_MCR);
> + /* Clear MRSET and RFSH bit */
> + word &= ~(BSC_MCR_MRSET | BSC_MCR_RFSH);
> + pcic_writel(word, pcic, PCIC_PCIMCR);
> +
> + return 0;
> +}
> +
> +#define NUM_AREA 7
> +static int set_pci_ranges(struct device *dev,
> + void __iomem *pcic, void __iomem *bsc, bool bcr3)
> +{
> + struct resource_entry *dma, *tmp;
> + struct pci_host_bridge *bridge;
> + u32 bsc_done[NUM_AREA];
> + unsigned int la;
> +
> + bridge = dev_get_drvdata(dev);
> + pcic_writel(0, pcic, PCIC_PCILAR0);
> + pcic_writel(0, pcic, PCIC_PCILAR1);
> + la = 0;
> + memset(&bsc_done, 0, sizeof(bsc_done));
> + resource_list_for_each_entry_safe(dma, tmp, &bridge->dma_ranges) {
> + struct resource *res = dma->res;
> + unsigned int area;
> + u32 word;
> +
> + switch (resource_type(res)) {
> + case IORESOURCE_IO:
> + /* BAR0 is I/O space */
> + word = res->start | 1;
> + pcic_writel(word, pcic, PCI_BASE_ADDRESS_0);
> + word = pcic_readl(pcic, PCI_COMMAND);
> + word |= PCI_COMMAND_IO;
> + pcic_writel(word, pcic, PCI_COMMAND);
> + break;
> + case IORESOURCE_MEM:
> + if (la > 4) {
> + dev_err(dev, "Invalid range definition.\n");
> + return -EINVAL;
> + }
> + area = (res->start >> 26) & 0x07;
> + word = res->end - res->start;
> + if (area >= NUM_AREA) {
> + /* Area 7 is reserved. */
> + dev_info(dev, "Invalid local address 0x%08x. Ignore it.\n",
> + res->start);
> + break;
> + }
> + pcic_writel(res->start, pcic, PCI_BASE_ADDRESS_1 + la);
> + /* if dummy entry, skip BSC setup */
> + if (word < 4)
> + break;
> + /* BAR1 is local area 0, BAR2 is local area 1 */
> + pcic_writel(word, pcic, PCIC_PCILSR0 + la);
> + word = P2SEGADDR(res->start);
> + pcic_writel(word, pcic, PCIC_PCILAR0 + la);
> + la += 4;
> + if (!bsc_done[area]) {
> + /* check BCR for SDRAM in specified area. And setup PCI BSC. */
> + if (setup_pci_bsc(dev, pcic, bsc, area, bcr3))
> + return -EINVAL;
> + bsc_done[area] = 1;
> + }
> + break;
> + }
> + }
> + return 0;
> +}
> +
> +static int sh7751_pci_probe(struct platform_device *pdev)
> +{
> + struct resource *res, *bscres;
> + void __iomem *pcic;
> + void __iomem *bsc;
> + u16 vid, did;
> + u32 word;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (IS_ERR(res))
> + return PTR_ERR(res);
> + pcic = ioremap(res->start, res->end - res->start + 1);
Can you consider using devm_platform_ioremap_resource() API ?
> + bscres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + bsc = devm_ioremap_resource(&pdev->dev, bscres);
> + if (IS_ERR(bsc))
> + return PTR_ERR(bsc);
Same as above.
> + /* check for SH7751/SH7751R hardware */
> + word = pcic_readl(pcic, PCI_VENDOR_ID);
> + vid = word & 0xffff;
> + did = word >> 16;
> + if ((vid != PCI_VENDOR_ID_HITACHI) ||
> + ((did != PCI_DEVICE_ID_SH7751) &&
> + (did != PCI_DEVICE_ID_SH7751R))) {
> + dev_err(&pdev->dev, "This is not an SH7751(R)\n");
error handling missing iounmap(pcic)
> + return -ENODEV;
> + }
> + dev_info(&pdev->dev, "PCI core found at %pR\n", res);
> +
> + /* Set the BCR's to enable PCI access */
> + word = __raw_readl(bsc + BSC_BCR1);
> + word |= BSC_BCR1_BRQEN;
> + __raw_writel(word, bsc + BSC_BCR1);
> +
> + /* Turn the clocks back on (not done in reset)*/
> + pcic_writel(PCIC_WE_KEY | 0, pcic, PCIC_PCICLKR);
> + /* Clear Powerdown IRQ's (not done in reset) */
> + word = PCIC_PCIPINT_D3 | PCIC_PCIPINT_D0;
> + pcic_writel(word, pcic, PCIC_PCIPINT);
> +
> + /* set the command/status */
> + word = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
> + PCI_COMMAND_PARITY | PCI_COMMAND_WAIT;
> + pcic_writel(word, pcic, PCI_COMMAND);
> +
> + /* define this host as the host bridge */
> + word = PCI_BASE_CLASS_BRIDGE << 24;
> + pcic_writel(word, pcic, PCI_CLASS_REVISION);
> +
> + ret = pci_host_common_probe(pdev);
> + if (ret) {
> + dev_err(&pdev->dev, "Initialize failed (%d)\n", ret);
> + return ret;
> + }
> +
> + /* Set IO and Mem windows to local address */
> + if (set_pci_ranges(&pdev->dev, pcic, bsc,
> + did == PCI_DEVICE_ID_SH7751R))
> + return -EINVAL;
error handling to call pci_host_common_remove() ?
> + pcic_writel(0, pcic, PCIC_PCIIOBR);
> +
> + if (of_property_read_bool(pdev->dev.of_node, "renesas,bus-arbit-round-robin"))
> + word |= BIT(0);
> + else
> + word = 0;
> + pcic_writel(word, pcic, PCIC_PCIDMABT);
> +
> + /* SH7751 init done, set central function init complete */
> + /* use round robin mode to stop a device starving/overrunning */
multi-line comment ?
> + word = PCIC_PCICR_CFIN | PCIC_PCICR_ARBM;
> + pcic_writel(PCIC_WE_KEY | word, pcic, PCIC_PCICR);
> +
> + return 0;
> +}
> +
> +/*
> + * Direct access to PCI hardware...
> + */
> +#define CONFIG_CMD(bus, devfn, where) \
> + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
> +
> +static void __iomem *sh4_pci_map_bus(struct pci_bus *bus,
> + unsigned int devfn, int where)
> +{
> + struct pci_config_window *cfg = bus->sysdata;
> + void __iomem *pcic = (void __iomem *)cfg->res.start;
> +
> + pcic_writel(CONFIG_CMD(bus, devfn, where), pcic, PCIC_PCIPAR);
> + return pcic + PCIC_PCIPDR;
> +}
> +
> +static const struct pci_ecam_ops pci_sh7751_bus_ops = {
> + .pci_ops = {
> + .map_bus = sh4_pci_map_bus,
> + .read = pci_generic_config_read32,
> + .write = pci_generic_config_write32,
> + }
> +};
> +
> +static const struct of_device_id sh7751_pci_of_match[] = {
> + { .compatible = "renesas,sh7751-pci",
> + .data = &pci_sh7751_bus_ops },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, sh7751_pci_of_match);
> +
> +static struct platform_driver sh7751_pci_driver = {
> + .driver = {
> + .name = "sh7751-pci",
> + .of_match_table = sh7751_pci_of_match,
> + },
> + .probe = sh7751_pci_probe,
> +};
> +module_platform_driver(sh7751_pci_driver);
> +
> +MODULE_DESCRIPTION("SH7751 PCI driver");