Files
thead-kernel/drivers/soc/thead/light-iopmp.c
thead_admin e17ac7bab2 Linux_SDK_V1.3.3
Signed-off-by: thead_admin <occ_thead@service.alibaba.com>
2024-01-31 22:09:29 +08:00

709 lines
21 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021 Alibaba Group Holding Limited.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/regulator/consumer.h>
#include <linux/of_platform.h>
#include <linux/err.h>
#include <dt-bindings/soc/thead,light-iopmp.h>
#define LIGHT_PMP_MISC_CTRL 0x04
#define LIGHT_PMP_DUMMY_ADDR 0x08
#define LIGHT_PMP_PAGE_LOCK0 0x40
#define LIGHT_PMP_ATTR_CFG0 0x80
#define LIGHT_DEFAULT_PMP_ATTR_CFG 0xc0
#define LIGHT_PMP_REGION0_SADDR 0x280
#define LIGHT_PMP_REGION0_EADDR 0x284
#define LIGHT_PMP_PAGE_LOCK0_REGIONS 0xFFFF
#define LIGHT_PMP_PAGE_LOCK0_DEFAULT_CFG BIT(16)
#define LIGHT_PMP_PAGE_LOCK0_BYPASS_EN BIT(17)
#define LIGHT_PMP_PAGE_LOCK0_DUMMY_ADDR BIT(18)
#define LIGHT_IOPMP_REGION_NUM 16
#define LIGHT_IOPMP_ATTR_CFG_DEFAULT 0xFFFFFFFF
#define LIGHT_IOPMP_DUMMY_ADDR 0x800000
#define LIGHT_IOPMP_CTRL_BYPASS 0xFF000000
struct light_iopmp_entry {
u32 reg_start[LIGHT_IOPMP_REGION_NUM];
u32 reg_end[LIGHT_IOPMP_REGION_NUM];
u32 entry_valid_num;
};
struct light_iopmp_cur_sys_entry {
int iopmp_type;
u32 start_addr;
u32 end_addr;
u32 attr;
int lock;
};
struct light_iopmp_info {
struct device *dev;
int entries;
struct light_iopmp_list *iopmp_list;
struct light_iopmp_cur_sys_entry cur_entry;
struct light_iopmp_entry iopmp_entry[];
};
static struct light_iopmp_list {
int iopmp_type;
resource_size_t offset;
void __iomem *base;
bool bypass_en;
bool is_default_region;
u32 dummy_slave;
u32 attr;
int lock;
struct light_iopmp_entry iopmp_entry;
} light_iopmp_lists[] = {
{IOPMP_EMMC, 0xFFFC028000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_SDIO0, 0xFFFC029000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_SDIO1, 0xFFFC02A000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_USB0, 0xFFFC02E000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_AO, 0xFFFFC21000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_AUD, 0xFFFFC22000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_CHIP_DBG, 0xFFFFC37000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_EIP120I, 0xFFFF220000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_EIP120II, 0xFFFF230000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_EIP120III, 0xFFFF240000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_ISP0, 0xFFF4080000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_ISP1, 0xFFF4081000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_DW200, 0xFFF4082000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_VIPRE, 0xFFF4083000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_VENC, 0xFFFCC60000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_VDEC, 0xFFFCC61000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_G2D, 0xFFFCC62000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_FCE, 0xFFFCC63000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_NPU, 0xFFFF01C000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP0_DPU, 0xFFFF520000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP1_DPU, 0xFFFF521000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_GPU, 0xFFFF522000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_GMAC1, 0xFFFC001000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_GMAC2, 0xFFFC002000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_DMAC, 0xFFFFC20000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_TEE_DMAC, 0xFFFF250000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_DSP0, 0xFFFF058000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
{IOPMP_DSP1, 0xFFFF059000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
};
static const struct light_iopmp_driver_data {
struct light_iopmp_list *iopmp_list;
} light_iopmp_data = {
.iopmp_list = light_iopmp_lists,
};
static int light_iopmp_get_entries(struct light_iopmp_list *match)
{
if (match == light_iopmp_lists)
return ARRAY_SIZE(light_iopmp_lists);
return 0;
}
static int light_iopmp_get_type(struct light_iopmp_info *info, struct device_node *entry_np)
{
int entry_size = info->entries;
struct light_iopmp_list *match = info->iopmp_list;
unsigned long type;
if (kstrtoul(entry_np->name, 0, &type))
return -EINVAL;
if (type < match[0].iopmp_type || type > match[entry_size - 1].iopmp_type)
return -EINVAL;
return type;
}
static bool light_iopmp_type_valid(struct light_iopmp_info *info, int tap)
{
int entry_size = info->entries;
struct light_iopmp_list *match = info->iopmp_list;
if (tap < match[0].iopmp_type || tap > match[entry_size - 1].iopmp_type)
return false;
return true;
}
static void light_iopmp_set_attr_bypass(struct light_iopmp_info *info, int type)
{
void __iomem *regs;
u32 val;
if (!info->iopmp_list[type].base) {
regs = devm_ioremap(info->dev, info->iopmp_list[type].offset, PAGE_SIZE);
if (!regs) {
dev_err(info->dev, "ioremap failed\n");
return;
}
info->iopmp_list[type].base = regs;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
if (val & LIGHT_PMP_PAGE_LOCK0_BYPASS_EN) {
dev_info(info->dev, "iopmp bypass_en is already locked, ignore the tap[%d] setting\n", type);
return;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_MISC_CTRL);
val |= LIGHT_IOPMP_CTRL_BYPASS;
writel(val, info->iopmp_list[type].base + LIGHT_PMP_MISC_CTRL);
/* lock the iopmp */
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
val |= LIGHT_PMP_PAGE_LOCK0_BYPASS_EN;
writel(val, info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
}
static void light_iopmp_set_attr_default(struct light_iopmp_info *info, int type, int attr)
{
void __iomem *regs;
u32 val;
if (!info->iopmp_list[type].base) {
regs = devm_ioremap(info->dev, info->iopmp_list[type].offset, PAGE_SIZE);
if (!regs) {
dev_err(info->dev, "ioremap failed\n");
return;
}
info->iopmp_list[type].base = regs;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
if (val & LIGHT_PMP_PAGE_LOCK0_DEFAULT_CFG) {
dev_info(info->dev, "iopmp default cfg is already locked, ignore the tap[%d] setting\n", type);
return;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_MISC_CTRL);
if (val & LIGHT_IOPMP_CTRL_BYPASS) {
dev_info(info->dev, "iopmp bypass is already enabled, ignore the tap[%d] setting\n", type);
return;
}
writel(attr, info->iopmp_list[type].base + LIGHT_DEFAULT_PMP_ATTR_CFG);
/* lock the iopmp */
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
val |= LIGHT_PMP_PAGE_LOCK0_DEFAULT_CFG;
writel(val, info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
}
static void light_iopmp_set_attr(struct light_iopmp_info *info, int type, int attr,
int dummy_slave, struct light_iopmp_entry *entry)
{
void __iomem *regs;
bool dummy_slave_lock = false;
u32 val;
int i;
if (!info->iopmp_list[type].base) {
regs = devm_ioremap(info->dev, info->iopmp_list[type].offset, PAGE_SIZE);
if (!regs) {
dev_err(info->dev, "ioremap failed\n");
return;
}
info->iopmp_list[type].base = regs;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
if (val & LIGHT_PMP_PAGE_LOCK0_REGIONS) {
dev_info(info->dev, "iopmp regions cfg is already locked, ignore the tap[%d] setting\n", type);
return;
}
if (val & LIGHT_PMP_PAGE_LOCK0_DUMMY_ADDR)
dummy_slave_lock = true;
val = readl(info->iopmp_list[type].base + LIGHT_PMP_MISC_CTRL);
if (val & LIGHT_IOPMP_CTRL_BYPASS) {
dev_info(info->dev, "iopmp bypass is already enabled, ignore the tap[%d] setting\n", type);
return;
}
for (i = 0; i < entry->entry_valid_num; i++) {
writel(entry->reg_start[i],
info->iopmp_list[type].base + LIGHT_PMP_REGION0_SADDR + (i << 3));
writel(entry->reg_end[i],
info->iopmp_list[type].base + LIGHT_PMP_REGION0_EADDR + (i << 3));
writel(attr, info->iopmp_list[type].base + LIGHT_PMP_ATTR_CFG0 + (i << 2));
}
if (!dummy_slave_lock)
writel(dummy_slave, info->iopmp_list[type].base + LIGHT_PMP_DUMMY_ADDR);
/* lock the iopmp */
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
val |= LIGHT_PMP_PAGE_LOCK0_REGIONS | LIGHT_PMP_PAGE_LOCK0_DUMMY_ADDR;
writel(val, info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
}
static int light_iopmp_sys_set(struct light_iopmp_info *info)
{
void __iomem *regs;
int type = info->cur_entry.iopmp_type;
bool dummy_slave_lock = false;
u32 val;
if (!info->iopmp_list[type].base) {
regs = devm_ioremap(info->dev, info->iopmp_list[type].offset, PAGE_SIZE);
if (!regs) {
dev_err(info->dev, "ioremap failed\n");
return -ENOMEM;
}
info->iopmp_list[type].base = regs;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_MISC_CTRL);
if (val & LIGHT_IOPMP_CTRL_BYPASS) {
dev_err(info->dev, "iopmp bypass is already enabled, ignore the tap[%d] setting\n", type);
return -EINVAL;
}
val = readl(info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
if (val & LIGHT_PMP_PAGE_LOCK0_REGIONS) {
dev_err(info->dev, "iopmp regions cfg is already locked, ignore the tap[%d] setting\n", type);
return -EINVAL;
}
if (val & LIGHT_PMP_PAGE_LOCK0_DUMMY_ADDR)
dummy_slave_lock = true;
val = readl(info->iopmp_list[type].base + LIGHT_PMP_MISC_CTRL);
if (val & LIGHT_IOPMP_CTRL_BYPASS) {
dev_err(info->dev, "iopmp bypass is already enabled, ignore the tap[%d] setting\n", type);
return -EINVAL;
}
/* set one region from userspace's config */
writel(info->cur_entry.start_addr,
info->iopmp_list[type].base + LIGHT_PMP_REGION0_SADDR);
writel(info->cur_entry.end_addr,
info->iopmp_list[type].base + LIGHT_PMP_REGION0_EADDR);
writel(info->cur_entry.attr, info->iopmp_list[type].base + LIGHT_PMP_ATTR_CFG0);
if (!dummy_slave_lock)
writel(LIGHT_IOPMP_DUMMY_ADDR, info->iopmp_list[type].base + LIGHT_PMP_DUMMY_ADDR);
/* lock the iopmp */
if (info->cur_entry.lock) {
val = LIGHT_PMP_PAGE_LOCK0_REGIONS | LIGHT_PMP_PAGE_LOCK0_DEFAULT_CFG |
LIGHT_PMP_PAGE_LOCK0_DUMMY_ADDR | LIGHT_PMP_PAGE_LOCK0_BYPASS_EN;
writel(val, info->iopmp_list[type].base + LIGHT_PMP_PAGE_LOCK0);
}
return 0;
}
static ssize_t light_iopmp_tap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
ssize_t ret;
ret = sprintf(buf, "%d\n", info->cur_entry.iopmp_type);
return ret;
}
static ssize_t light_iopmp_start_addr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
ssize_t ret;
ret = sprintf(buf, "0x%x\n", info->cur_entry.start_addr);
return ret;
}
static ssize_t light_iopmp_end_addr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
ssize_t ret;
ret = sprintf(buf, "0x%x\n", info->cur_entry.end_addr);
return ret;
}
static ssize_t light_iopmp_attr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
ssize_t ret;
ret = sprintf(buf, "0x%x\n", info->cur_entry.attr);
return ret;
}
static ssize_t light_iopmp_lock_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
ssize_t ret;
ret = sprintf(buf, "%s\n", info->cur_entry.lock ? "locked" : "no lock");
return ret;
}
static ssize_t light_iopmp_tap_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (light_iopmp_type_valid(info, (int)val))
info->cur_entry.iopmp_type = val;
else
dev_err(dev, "invalid tap value\n");
return count;
}
static ssize_t light_iopmp_start_addr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
info->cur_entry.start_addr = (u32)val;
return count;
}
static ssize_t light_iopmp_end_addr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (!val)
return -EINVAL;
info->cur_entry.end_addr = (u32)val;
return count;
}
static ssize_t light_iopmp_attr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
info->cur_entry.attr = (u32)val;
return count;
}
static ssize_t light_iopmp_lock_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
info->cur_entry.lock = (int)val;
return count;
}
static ssize_t light_iopmp_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
unsigned long val;
int ret;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (!val)
return count;
if (!light_iopmp_type_valid(info, info->cur_entry.iopmp_type)) {
dev_err(dev, "invalid iopmp tap ID\n");
return -EINVAL;
}
if (info->cur_entry.end_addr <= info->cur_entry.start_addr) {
dev_err(dev, "invalid end_addr/start_addr setting\n");
return -EINVAL;
}
ret = light_iopmp_sys_set(info);
if (ret)
return -EINVAL;
return count;
}
static int light_iopmp_config(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct light_iopmp_info *info = platform_get_drvdata(pdev);
struct light_iopmp_list *list = info->iopmp_list;
int i, j;
for (i = 0; i < info->entries; i++) {
if (list[i].iopmp_entry.entry_valid_num > 0) {
/* config the iopmp entry */
light_iopmp_set_attr(info, i,
list[i].attr,
list[i].dummy_slave,
&list[i].iopmp_entry);
} else {
/* config the iopmp entry */
if (list[i].bypass_en)
light_iopmp_set_attr_bypass(info, i);
else if (list[i].is_default_region)
light_iopmp_set_attr_default(info, i, list[i].attr);
}
}
return 0;
}
static DEVICE_ATTR(light_iopmp_tap, 0644, light_iopmp_tap_show, light_iopmp_tap_store);
static DEVICE_ATTR(light_iopmp_start_addr, 0644, light_iopmp_start_addr_show, light_iopmp_start_addr_store);
static DEVICE_ATTR(light_iopmp_end_addr, 0644, light_iopmp_end_addr_show, light_iopmp_end_addr_store);
static DEVICE_ATTR(light_iopmp_attr, 0644, light_iopmp_attr_show, light_iopmp_attr_store);
static DEVICE_ATTR(light_iopmp_lock, 0644, light_iopmp_lock_show, light_iopmp_lock_store);
static DEVICE_ATTR(light_iopmp_set, 0644, NULL, light_iopmp_set_store);
static struct attribute *light_iopmp_sysfs_entries[] = {
&dev_attr_light_iopmp_tap.attr,
&dev_attr_light_iopmp_start_addr.attr,
&dev_attr_light_iopmp_end_addr.attr,
&dev_attr_light_iopmp_attr.attr,
&dev_attr_light_iopmp_lock.attr,
&dev_attr_light_iopmp_set.attr,
NULL
};
static const struct attribute_group dev_attr_light_iopmp_group = {
.attrs = light_iopmp_sysfs_entries,
};
static int light_iopmp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct light_iopmp_driver_data *drvdata = NULL;
struct light_iopmp_list *list;
struct light_iopmp_info *info;
struct device_node *entry_np;
int size, entries;
int ret;
int i, j;
drvdata = device_get_match_data(dev);
if (!drvdata) {
dev_err(dev, "cannot get driver data\n");
return -ENOENT;
}
list = drvdata->iopmp_list;
entries = light_iopmp_get_entries(list);
if (entries <= 0)
return -ENOENT;
size = sizeof(struct light_iopmp_entry) * entries + sizeof(*info);
info = devm_kzalloc(dev, size, GFP_KERNEL);
if (!info)
return -ENOMEM;
info->entries = entries;
info->iopmp_list = list;
info->dev = dev;
for_each_child_of_node(dev->of_node, entry_np) {
int attr = LIGHT_IOPMP_ATTR_CFG_DEFAULT;
int dummy_slave = LIGHT_IOPMP_DUMMY_ADDR;
bool is_default_region = false;
bool bypass_en = false;
int region_size;
int type;
if (!of_device_is_available(entry_np))
continue;
type = light_iopmp_get_type(info, entry_np);
if (type < 0) {
dev_err(dev, "invalid iopmp tap:%d\n", type);
continue;
}
ret = of_property_read_u32(entry_np, "attr", &attr);
if (!ret && (attr == 0))
attr = LIGHT_IOPMP_ATTR_CFG_DEFAULT;
ret = of_property_read_u32(entry_np, "dummy_slave", &dummy_slave);
if (!ret && (dummy_slave == 0))
dummy_slave = LIGHT_IOPMP_DUMMY_ADDR;
region_size = of_property_count_elems_of_size(entry_np, "regions", sizeof(u32));
if (region_size <= 0) {
is_default_region = of_property_read_bool(entry_np, "is_default_region");
bypass_en = of_property_read_bool(entry_np, "bypass_en");
if (!is_default_region && !bypass_en) {
dev_err(dev, "missing/invalid reg property\n");
continue;
}
} else {
region_size >>= 1;
if (region_size > LIGHT_IOPMP_REGION_NUM) {
dev_err(dev, "region size is invalid\n");
continue;
}
for (j = 0; j < region_size; j++) {
ret = of_property_read_u32_index(entry_np, "regions",
j << 1, &list[type].iopmp_entry.reg_start[j]);
if (ret)
break;
ret = of_property_read_u32_index(entry_np, "regions",
(j << 1) + 1, &list[type].iopmp_entry.reg_end[j]);
if (ret)
break;
/* check the region valid, otherwise drop the iopmp setting */
if (j && list[type].iopmp_entry.reg_end[j - 1] > list[type].iopmp_entry.reg_start[j])
break;
}
if (j < region_size)
continue;
list[type].iopmp_entry.entry_valid_num = region_size;
}
// store valid config
list[type].attr = attr;
list[type].dummy_slave = dummy_slave;
list[type].is_default_region = is_default_region;
list[type].bypass_en = bypass_en;
}
/* init the cur entry config */
info->cur_entry.iopmp_type = -1;
info->cur_entry.start_addr = 0;
info->cur_entry.end_addr = 0;
info->cur_entry.attr = 0;
info->cur_entry.lock = false;
platform_set_drvdata(pdev, info);
light_iopmp_config(&pdev->dev);
ret = sysfs_create_group(&pdev->dev.kobj, &dev_attr_light_iopmp_group);
if (ret) {
dev_err(&pdev->dev, "Failed to create light iopmp debug sysfs.\n");
return ret;
}
return 0;
}
static int light_iopmp_remove(struct platform_device *pdev)
{
platform_set_drvdata(pdev, NULL);
return 0;
}
static int __maybe_unused light_iopmp_noirq_suspend(struct device *dev)
{
// nothing to do
return 0;
}
static int __maybe_unused light_iopmp_noirq_resume(struct device *dev)
{
/* reinit iopmp register setting
ensure system clock enabled before here
*/
light_iopmp_config(dev);
return 0;
}
static const struct dev_pm_ops light_iopmp_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(light_iopmp_noirq_suspend, light_iopmp_noirq_resume)
};
static const struct of_device_id light_iopmp_of_match[] = {
{ .compatible = "thead,light-iopmp", .data = &light_iopmp_data},
{},
};
static struct platform_driver light_iopmp_driver = {
.driver = {
.name = "light-iopmp",
.of_match_table = of_match_ptr(light_iopmp_of_match),
.pm = &light_iopmp_pm_ops,
},
.probe = light_iopmp_probe,
.remove = light_iopmp_remove,
};
static int __init light_iopmp_init(void)
{
return platform_driver_register(&light_iopmp_driver);
}
arch_initcall(light_iopmp_init);
MODULE_ALIAS("platform:light-iopmp");
MODULE_AUTHOR("fugang.duan <duanfugang.dfg@linux.alibaba.com>");
MODULE_DESCRIPTION("Thead Light iopmp driver");
MODULE_LICENSE("GPL v2");