Skip to content

Commit 6abccd4

Browse files
JiakunShuaiopsiff
authored andcommitted
bus: phytium-pio: Add support for legacy I/O operation
This Phytium PIO controller driver makes it possible for Socs to access legacy I/O ports. Signed-off-by: Feng Jun <fengjun@phytium.com.cn> Signed-off-by: Wang Yinfeng <wangyinfeng@phytium.com.cn> Signed-off-by: Jiakun Shuai <shuaijiakun1288@phytium.com.cn>
1 parent e2c820f commit 6abccd4

5 files changed

Lines changed: 325 additions & 2 deletions

File tree

drivers/bus/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@ config OMAP_OCP2SCP
142142
OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via
143143
OCP2SCP.
144144

145+
config PHYTIUM_PIO
146+
bool "Support for ISA I/O space on Phytium SoCs"
147+
depends on (ARM64 && ARCH_PHYTIUM)
148+
depends on HAS_IOMEM
149+
select INDIRECT_PIO if ARM64
150+
help
151+
Driver to enable I/O access to devices attached to the Legacy IO ports
152+
on the Phytium SoCs.
153+
145154
config QCOM_EBI2
146155
bool "Qualcomm External Bus Interface 2 (EBI2)"
147156
depends on HAS_IOMEM

drivers/bus/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# Interconnect bus drivers for ARM platforms
77
obj-$(CONFIG_ARM_CCI) += arm-cci.o
88
obj-$(CONFIG_ARM_INTEGRATOR_LM) += arm-integrator-lm.o
9+
obj-$(CONFIG_PHYTIUM_PIO) += phytium_pio.o
910
obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o
1011
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
1112
obj-$(CONFIG_MOXTET) += moxtet.o

drivers/bus/phytium_pio.c

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Phytium PIO driver
4+
*
5+
* Copyright (C) 2024 Phytium Technology Co., Ltd. All Rights Reserved.
6+
*/
7+
8+
#include <linux/acpi.h>
9+
#include <linux/io.h>
10+
#include <linux/logic_pio.h>
11+
#include <linux/module.h>
12+
#include <linux/spinlock.h>
13+
#include <linux/of.h>
14+
#include <linux/of_platform.h>
15+
#include <linux/platform_device.h>
16+
#include "phytium_pio.h"
17+
18+
#define PHYT_PIO_DRIVER_NAME "phytium-pio"
19+
#define PHYT_PIO_DRV_VER "1.1.0"
20+
21+
static struct phytium_pio *ppio;
22+
23+
bool check_cpu_type(void)
24+
{
25+
if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM)
26+
return true;
27+
return false;
28+
}
29+
EXPORT_SYMBOL(check_cpu_type);
30+
31+
void phytium_pio_clear_interrupt(u32 val)
32+
{
33+
unsigned long flags;
34+
35+
if (!ppio)
36+
return;
37+
spin_lock_irqsave(&ppio->lock, flags);
38+
writew(val, ppio->membase + PHYTIUM_PIO_CLR_INT);
39+
spin_unlock_irqrestore(&ppio->lock, flags);
40+
}
41+
EXPORT_SYMBOL(phytium_pio_clear_interrupt);
42+
43+
int phytium_pio_get_irq(void)
44+
{
45+
if (!ppio)
46+
return -EINVAL;
47+
return ppio->irq;
48+
}
49+
EXPORT_SYMBOL(phytium_pio_get_irq);
50+
51+
/*
52+
* phytium_pio_in - input the data from the target I/O port
53+
* @hostdata: pointer to the device information relevant to PIO controller
54+
* @addr: the target I/O port address
55+
* @dwidth: the data width required reading from the target I/O port
56+
*
57+
* Return the data read back on success, -ERRNO otherwise.
58+
*/
59+
static u32 phytium_pio_in(void *hostdata, unsigned long addr, size_t dwidth)
60+
{
61+
struct phytium_pio *pio = hostdata;
62+
struct logic_pio_hwaddr *range = pio->range;
63+
__le32 rd_data = 0;
64+
unsigned long flags;
65+
66+
spin_lock_irqsave(&pio->lock, flags);
67+
switch (dwidth) {
68+
case 1:
69+
rd_data = readb(pio->membase + addr - range->io_start);
70+
break;
71+
case 2:
72+
rd_data = readw(pio->membase + addr - range->io_start);
73+
break;
74+
case 4:
75+
rd_data = readl(pio->membase + addr - range->io_start);
76+
break;
77+
default:
78+
break;
79+
}
80+
spin_unlock_irqrestore(&pio->lock, flags);
81+
return le32_to_cpu(rd_data);
82+
}
83+
84+
/*
85+
* phytium_pio_out - output the data to the target I/O port
86+
* @hostdata: pointer to the device information relevant to PIO controller
87+
* @addr: the target I/O port address
88+
* @val: the data to be output
89+
* @dwidth: the data width required writing to the target I/O port
90+
*/
91+
static void phytium_pio_out(void *hostdata, unsigned long addr, u32 val,
92+
size_t dwidth)
93+
{
94+
struct phytium_pio *pio = hostdata;
95+
struct logic_pio_hwaddr *range = pio->range;
96+
unsigned long flags;
97+
98+
spin_lock_irqsave(&pio->lock, flags);
99+
switch (dwidth) {
100+
case 1:
101+
writeb(val, pio->membase + addr - range->io_start);
102+
break;
103+
case 2:
104+
writew(val, pio->membase + addr - range->io_start);
105+
break;
106+
case 4:
107+
writel(val, pio->membase + addr - range->io_start);
108+
break;
109+
default:
110+
break;
111+
}
112+
spin_unlock_irqrestore(&pio->lock, flags);
113+
}
114+
115+
/*
116+
* phytium_pio_ins - input the data in the buffer in multiple operations
117+
* @hostdata: pointer to the device information relevant to PIO controller
118+
* @pio: the target I/O port address
119+
* @buffer: the buffer to store the data
120+
* @dwidth: the data width required reading from the target I/O port
121+
* @count: the number of data to be read
122+
*
123+
* When success, the data read back is stored in buffer pointed by buffer.
124+
* Return 0 on success, -ERRNO otherwise.
125+
*/
126+
static u32 phytium_pio_ins(void *hostdata, unsigned long addr, void *buffer,
127+
size_t dwidth, unsigned int count)
128+
{
129+
struct phytium_pio *pio = hostdata;
130+
unsigned char *buf = buffer;
131+
132+
if (!pio || !buf || !count || !dwidth || dwidth > 4)
133+
return -EINVAL;
134+
135+
do {
136+
*(u32 *)buf = phytium_pio_in(pio, addr, dwidth);
137+
buf += dwidth;
138+
addr += dwidth;
139+
} while (--count);
140+
141+
return 0;
142+
}
143+
144+
/*
145+
* phytium_pio_outs - output the data in the buffer in multiple operations
146+
* @hostdata: pointer to the device information relevant to PIO controller
147+
* @addr: the target I/O port address
148+
* @buffer: the buffer to store the data
149+
* @dwidth: the data width required writing to the target I/O port
150+
* @count: the number of data to be written
151+
*/
152+
static void phytium_pio_outs(void *hostdata, unsigned long addr,
153+
const void *buffer, size_t dwidth, unsigned int count)
154+
{
155+
struct phytium_pio *pio = hostdata;
156+
const unsigned char *buf = buffer;
157+
158+
if (!pio || !buf || !count || !dwidth || dwidth > 4)
159+
return;
160+
161+
do {
162+
phytium_pio_out(pio, addr, *(u32 *)buf, dwidth);
163+
buf += dwidth;
164+
addr += dwidth;
165+
} while (--count);
166+
}
167+
168+
static const struct logic_pio_host_ops phytium_pio_ops = {
169+
.in = phytium_pio_in,
170+
.ins = phytium_pio_ins,
171+
.out = phytium_pio_out,
172+
.outs = phytium_pio_outs,
173+
};
174+
175+
static int phytium_pio_probe(struct platform_device *pdev)
176+
{
177+
struct device *dev = &pdev->dev;
178+
struct logic_pio_hwaddr *range;
179+
struct phytium_pio *pio;
180+
int ret;
181+
182+
pio = devm_kzalloc(dev, sizeof(*pio), GFP_KERNEL);
183+
if (!pio)
184+
return -ENOMEM;
185+
pio->membase = devm_platform_ioremap_resource(pdev, 0);
186+
if (IS_ERR_OR_NULL(pio->membase))
187+
return PTR_ERR(pio->membase);
188+
189+
pio->irq = platform_get_irq(pdev, 0);
190+
if (pio->irq < 0) {
191+
dev_err(dev, "no irq resource?\n");
192+
return pio->irq;
193+
}
194+
195+
ret = device_property_read_u32(&pdev->dev, "int_state",
196+
&pio->int_status_reg);
197+
if (ret)
198+
pio->int_status_reg = PHYTIUM_PIO_INT_STATE;
199+
200+
ret = device_property_read_u32(&pdev->dev, "clr_int",
201+
&pio->int_clear_reg);
202+
if (ret)
203+
pio->int_clear_reg = PHYTIUM_PIO_CLR_INT;
204+
205+
range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
206+
if (!range)
207+
return -ENOMEM;
208+
range->fwnode = dev_fwnode(dev);
209+
range->flags = LOGIC_PIO_INDIRECT;
210+
range->size = PIO_INDIRECT_SIZE;
211+
range->hostdata = pio;
212+
range->ops = &phytium_pio_ops;
213+
pio->range = range;
214+
ppio = pio;
215+
ret = logic_pio_register_range(range);
216+
if (ret) {
217+
dev_err(dev, "Failed to register logic pio range (%d)!\n", ret);
218+
goto out;
219+
}
220+
spin_lock_init(&pio->lock);
221+
dev_set_drvdata(dev, pio);
222+
out:
223+
return ret;
224+
}
225+
226+
static int phytium_pio_remove(struct platform_device *pdev)
227+
{
228+
struct logic_pio_hwaddr *range;
229+
230+
range = find_io_range_by_fwnode(pdev->dev.fwnode);
231+
if (!range)
232+
return -EINVAL;
233+
234+
logic_pio_unregister_range(range);
235+
236+
return 0;
237+
}
238+
239+
static const struct acpi_device_id phytium_pio_acpi_match[] = {
240+
{ "PHYT0007", },
241+
{},
242+
};
243+
MODULE_DEVICE_TABLE(acpi, phytium_pio_acpi_match);
244+
245+
static struct platform_driver phytium_pio_driver = {
246+
.driver = {
247+
.name = PHYT_PIO_DRIVER_NAME,
248+
.acpi_match_table = ACPI_PTR(phytium_pio_acpi_match),
249+
},
250+
.probe = phytium_pio_probe,
251+
.remove = phytium_pio_remove,
252+
};
253+
254+
255+
static int __init phytium_pio_init(void)
256+
{
257+
return platform_driver_register(&phytium_pio_driver);
258+
}
259+
arch_initcall(phytium_pio_init);
260+
261+
MODULE_DESCRIPTION("Phytium pio driver");
262+
MODULE_VERSION(PHYT_PIO_DRV_VER);
263+
MODULE_LICENSE("GPL");
264+

drivers/bus/phytium_pio.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* bus/phytium_pio.h
4+
*
5+
* Copyright (C) 2021-2025, Phytium Technology Co., Ltd.
6+
*/
7+
8+
#ifndef _PHYTIUM_PIO_H_
9+
#define _PHYTIUM_PIO_H_
10+
11+
#include <linux/io.h>
12+
#include <linux/logic_pio.h>
13+
14+
#define PHYTIUM_PIO_INT_STATE 0x7FFFFC4
15+
#define PHYTIUM_PIO_CLR_INT 0x7FFFFC0
16+
#define LEGACY_ISA_SIZE 0x400
17+
18+
struct phytium_pio {
19+
void __iomem *membase;
20+
int irq;
21+
spinlock_t lock;
22+
u32 int_status_reg;
23+
u32 int_clear_reg;
24+
struct logic_pio_hwaddr *range;
25+
};
26+
27+
bool check_cpu_type(void);
28+
int phytium_pio_get_irq(void);
29+
void phytium_pio_clear_interrupt(u32 val);
30+
31+
#endif
32+
33+

lib/logic_pio.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
#include <linux/sizes.h>
1717
#include <linux/slab.h>
1818

19+
#ifdef CONFIG_PHYTIUM_PIO
20+
#include "../drivers/bus/phytium_pio.h"
21+
#endif
22+
23+
__weak bool check_cpu_type(void)
24+
{
25+
return false;
26+
}
1927
/* The unique hardware address list */
2028
static LIST_HEAD(io_range_list);
2129
static DEFINE_MUTEX(io_range_mutex);
@@ -234,6 +242,8 @@ type logic_in##bwl(unsigned long addr) \
234242
{ \
235243
type ret = (type)~0; \
236244
\
245+
if (check_cpu_type() == true) \
246+
addr += MMIO_UPPER_LIMIT; \
237247
if (addr < MMIO_UPPER_LIMIT) { \
238248
ret = _in##bwl(addr); \
239249
} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
@@ -242,14 +252,20 @@ type logic_in##bwl(unsigned long addr) \
242252
if (entry) \
243253
ret = entry->ops->in(entry->hostdata, \
244254
addr, sizeof(type)); \
245-
else \
246-
WARN_ON_ONCE(1); \
255+
else { \
256+
if (check_cpu_type() == true) \
257+
ret = 1; \
258+
else \
259+
WARN_ON_ONCE(1); \
260+
} \
247261
} \
248262
return ret; \
249263
} \
250264
\
251265
void logic_out##bwl(type value, unsigned long addr) \
252266
{ \
267+
if (check_cpu_type() == true) \
268+
addr += MMIO_UPPER_LIMIT; \
253269
if (addr < MMIO_UPPER_LIMIT) { \
254270
_out##bwl(value, addr); \
255271
} else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \

0 commit comments

Comments
 (0)