Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "Amlogic SoC drivers"
config MESON_CANVAS
tristate "Amlogic Meson Canvas driver"
depends on ARCH_MESON || COMPILE_TEST
default n
help
Say yes to support the canvas IP for Amlogic SoCs.
config MESON_CLK_MEASURE
tristate "Amlogic Meson SoC Clock Measure driver"
depends on ARCH_MESON || COMPILE_TEST
default ARCH_MESON
select REGMAP_MMIO
help
Say yes to support of Measuring a set of internal SoC clocks
from the debugfs interface.
config MESON_GX_SOCINFO
bool "Amlogic Meson GX SoC Information driver"
depends on (ARM64 && ARCH_MESON) || COMPILE_TEST
default ARCH_MESON
select SOC_BUS
help
Say yes to support decoding of Amlogic Meson GX SoC family
information about the type, package and version.
config MESON_MX_SOCINFO
bool "Amlogic Meson MX SoC Information driver"
depends on (ARM && ARCH_MESON) || COMPILE_TEST
default ARCH_MESON
select SOC_BUS
help
Say yes to support decoding of Amlogic Meson6, Meson8,
Meson8b and Meson8m2 SoC family information about the type
and version.
endmenu
+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o
obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 BayLibre, SAS
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
* Copyright (C) 2014 Endless Mobile
*/
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/soc/amlogic/meson-canvas.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#define NUM_CANVAS 256
/* DMC Registers */
#define DMC_CAV_LUT_DATAL 0x00
#define CANVAS_WIDTH_LBIT 29
#define CANVAS_WIDTH_LWID 3
#define DMC_CAV_LUT_DATAH 0x04
#define CANVAS_WIDTH_HBIT 0
#define CANVAS_HEIGHT_BIT 9
#define CANVAS_WRAP_BIT 22
#define CANVAS_BLKMODE_BIT 24
#define CANVAS_ENDIAN_BIT 26
#define DMC_CAV_LUT_ADDR 0x08
#define CANVAS_LUT_WR_EN BIT(9)
#define CANVAS_LUT_RD_EN BIT(8)
struct meson_canvas {
struct device *dev;
void __iomem *reg_base;
spinlock_t lock; /* canvas device lock */
u8 used[NUM_CANVAS];
bool supports_endianness;
};
static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
{
writel_relaxed(val, canvas->reg_base + reg);
}
static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
{
return readl_relaxed(canvas->reg_base + reg);
}
struct meson_canvas *meson_canvas_get(struct device *dev)
{
struct device_node *canvas_node;
struct platform_device *canvas_pdev;
struct meson_canvas *canvas;
canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
if (!canvas_node)
return ERR_PTR(-ENODEV);
canvas_pdev = of_find_device_by_node(canvas_node);
of_node_put(canvas_node);
if (!canvas_pdev)
return ERR_PTR(-EPROBE_DEFER);
/*
* If priv is NULL, it's probably because the canvas hasn't
* properly initialized. Bail out with -EINVAL because, in the
* current state, this driver probe cannot return -EPROBE_DEFER
*/
canvas = dev_get_drvdata(&canvas_pdev->dev);
put_device(&canvas_pdev->dev);
if (!canvas)
return ERR_PTR(-EINVAL);
return canvas;
}
EXPORT_SYMBOL_GPL(meson_canvas_get);
int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
u32 addr, u32 stride, u32 height,
unsigned int wrap,
unsigned int blkmode,
unsigned int endian)
{
unsigned long flags;
if (endian && !canvas->supports_endianness) {
dev_err(canvas->dev,
"Endianness is not supported on this SoC\n");
return -EINVAL;
}
spin_lock_irqsave(&canvas->lock, flags);
if (!canvas->used[canvas_index]) {
dev_err(canvas->dev,
"Trying to setup non allocated canvas %u\n",
canvas_index);
spin_unlock_irqrestore(&canvas->lock, flags);
return -EINVAL;
}
canvas_write(canvas, DMC_CAV_LUT_DATAL,
((addr + 7) >> 3) |
(((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
canvas_write(canvas, DMC_CAV_LUT_DATAH,
((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
CANVAS_WIDTH_HBIT) |
(height << CANVAS_HEIGHT_BIT) |
(wrap << CANVAS_WRAP_BIT) |
(blkmode << CANVAS_BLKMODE_BIT) |
(endian << CANVAS_ENDIAN_BIT));
canvas_write(canvas, DMC_CAV_LUT_ADDR,
CANVAS_LUT_WR_EN | canvas_index);
/* Force a read-back to make sure everything is flushed. */
canvas_read(canvas, DMC_CAV_LUT_DATAH);
spin_unlock_irqrestore(&canvas->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(meson_canvas_config);
int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
{
int i;
unsigned long flags;
spin_lock_irqsave(&canvas->lock, flags);
for (i = 0; i < NUM_CANVAS; ++i) {
if (!canvas->used[i]) {
canvas->used[i] = 1;
spin_unlock_irqrestore(&canvas->lock, flags);
*canvas_index = i;
return 0;
}
}
spin_unlock_irqrestore(&canvas->lock, flags);
dev_err(canvas->dev, "No more canvas available\n");
return -ENODEV;
}
EXPORT_SYMBOL_GPL(meson_canvas_alloc);
int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
{
unsigned long flags;
spin_lock_irqsave(&canvas->lock, flags);
if (!canvas->used[canvas_index]) {
dev_err(canvas->dev,
"Trying to free unused canvas %u\n", canvas_index);
spin_unlock_irqrestore(&canvas->lock, flags);
return -EINVAL;
}
canvas->used[canvas_index] = 0;
spin_unlock_irqrestore(&canvas->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(meson_canvas_free);
static int meson_canvas_probe(struct platform_device *pdev)
{
struct meson_canvas *canvas;
struct device *dev = &pdev->dev;
canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
if (!canvas)
return -ENOMEM;
canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(canvas->reg_base))
return PTR_ERR(canvas->reg_base);
canvas->supports_endianness = of_device_get_match_data(dev);
canvas->dev = dev;
spin_lock_init(&canvas->lock);
dev_set_drvdata(dev, canvas);
return 0;
}
static const struct of_device_id canvas_dt_match[] = {
{ .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
{ .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
{ .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
{ .compatible = "amlogic,canvas", .data = (void *)true, },
{}
};
MODULE_DEVICE_TABLE(of, canvas_dt_match);
static struct platform_driver meson_canvas_driver = {
.probe = meson_canvas_probe,
.driver = {
.name = "amlogic-canvas",
.of_match_table = canvas_dt_match,
},
};
module_platform_driver(meson_canvas_driver);
MODULE_DESCRIPTION("Amlogic Canvas driver");
MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,223 @@
/*
* Copyright (c) 2017 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#define AO_SEC_SD_CFG8 0xe0
#define AO_SEC_SOCINFO_OFFSET AO_SEC_SD_CFG8
#define SOCINFO_MAJOR GENMASK(31, 24)
#define SOCINFO_PACK GENMASK(23, 16)
#define SOCINFO_MINOR GENMASK(15, 8)
#define SOCINFO_MISC GENMASK(7, 0)
static const struct meson_gx_soc_id {
const char *name;
unsigned int id;
} soc_ids[] = {
{ "GXBB", 0x1f },
{ "GXTVBB", 0x20 },
{ "GXL", 0x21 },
{ "GXM", 0x22 },
{ "TXL", 0x23 },
{ "TXLX", 0x24 },
{ "AXG", 0x25 },
{ "GXLX", 0x26 },
{ "TXHD", 0x27 },
{ "G12A", 0x28 },
{ "G12B", 0x29 },
{ "SM1", 0x2b },
{ "A1", 0x2c },
{ "T7", 0x36 },
{ "S4", 0x37 },
{ "A5", 0x3c },
{ "C3", 0x3d },
{ "A4", 0x40 },
{ "S7", 0x46 },
{ "S7D", 0x47 },
{ "S6", 0x48 },
};
static const struct meson_gx_package_id {
const char *name;
unsigned int major_id;
unsigned int pack_id;
unsigned int pack_mask;
} soc_packages[] = {
{ "S905", 0x1f, 0, 0x20 }, /* pack_id != 0x20 */
{ "S905H", 0x1f, 0x3, 0xf }, /* pack_id & 0xf == 0x3 */
{ "S905M", 0x1f, 0x20, 0xf0 }, /* pack_id == 0x20 */
{ "S905D", 0x21, 0, 0xf0 },
{ "S905X", 0x21, 0x80, 0xf0 },
{ "S905W", 0x21, 0xa0, 0xf0 },
{ "S905L", 0x21, 0xc0, 0xf0 },
{ "S905M2", 0x21, 0xe0, 0xf0 },
{ "S805X", 0x21, 0x30, 0xf0 },
{ "S805Y", 0x21, 0xb0, 0xf0 },
{ "S912", 0x22, 0, 0x0 }, /* Only S912 is known for GXM */
{ "962X", 0x24, 0x10, 0xf0 },
{ "962E", 0x24, 0x20, 0xf0 },
{ "A113X", 0x25, 0x37, 0xff },
{ "A113X", 0x25, 0x43, 0xff },
{ "A113D", 0x25, 0x22, 0xff },
{ "S905L", 0x26, 0, 0x0 },
{ "S905D2", 0x28, 0x10, 0xf0 },
{ "S905Y2", 0x28, 0x30, 0xf0 },
{ "S905X2", 0x28, 0x40, 0xf0 },
{ "A311D", 0x29, 0x10, 0xf0 },
{ "S922X", 0x29, 0x40, 0xf0 },
{ "S905D3", 0x2b, 0x4, 0xf5 },
{ "S905X3", 0x2b, 0x5, 0xf5 },
{ "S905X3", 0x2b, 0x10, 0x3f },
{ "S905D3", 0x2b, 0x30, 0x3f },
{ "A113L", 0x2c, 0x0, 0xf8 },
{ "S805X2", 0x37, 0x2, 0xf },
{ "S905Y4", 0x37, 0x3, 0xf },
{ "C308L", 0x3d, 0x1, 0xf },
{ "A311D2", 0x36, 0x1, 0xf },
{ "A113X2", 0x3c, 0x1, 0xf },
{ "A113L2", 0x40, 0x1, 0xf },
{ "S805X3", 0x46, 0x3, 0xf },
{ "S905X5M", 0x47, 0x1, 0xf },
{ "S905X5", 0x48, 0x1, 0xf },
};
static inline unsigned int socinfo_to_major(u32 socinfo)
{
return FIELD_GET(SOCINFO_MAJOR, socinfo);
}
static inline unsigned int socinfo_to_minor(u32 socinfo)
{
return FIELD_GET(SOCINFO_MINOR, socinfo);
}
static inline unsigned int socinfo_to_pack(u32 socinfo)
{
return FIELD_GET(SOCINFO_PACK, socinfo);
}
static inline unsigned int socinfo_to_misc(u32 socinfo)
{
return FIELD_GET(SOCINFO_MISC, socinfo);
}
static const char *socinfo_to_package_id(u32 socinfo)
{
unsigned int pack = socinfo_to_pack(socinfo);
unsigned int major = socinfo_to_major(socinfo);
int i;
for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) {
if (soc_packages[i].major_id == major &&
soc_packages[i].pack_id ==
(pack & soc_packages[i].pack_mask))
return soc_packages[i].name;
}
return "Unknown";
}
static const char *socinfo_to_soc_id(u32 socinfo)
{
unsigned int id = socinfo_to_major(socinfo);
int i;
for (i = 0 ; i < ARRAY_SIZE(soc_ids) ; ++i) {
if (soc_ids[i].id == id)
return soc_ids[i].name;
}
return "Unknown";
}
static int __init meson_gx_socinfo_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device_node *np;
struct regmap *regmap;
unsigned int socinfo;
struct device *dev;
int ret;
/* look up for chipid node */
np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gx-ao-secure");
if (!np)
return -ENODEV;
/* check if interface is enabled */
if (!of_device_is_available(np)) {
of_node_put(np);
return -ENODEV;
}
/* check if chip-id is available */
if (!of_property_read_bool(np, "amlogic,has-chip-id")) {
of_node_put(np);
return -ENODEV;
}
/* node should be a syscon */
regmap = syscon_node_to_regmap(np);
of_node_put(np);
if (IS_ERR(regmap)) {
pr_err("%s: failed to get regmap\n", __func__);
return -ENODEV;
}
ret = regmap_read(regmap, AO_SEC_SOCINFO_OFFSET, &socinfo);
if (ret < 0)
return ret;
if (!socinfo) {
pr_err("%s: invalid chipid value\n", __func__);
return -EINVAL;
}
soc_dev_attr = kzalloc_obj(*soc_dev_attr);
if (!soc_dev_attr)
return -ENODEV;
soc_dev_attr->family = "Amlogic Meson";
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x:%x - %x:%x",
socinfo_to_major(socinfo),
socinfo_to_minor(socinfo),
socinfo_to_pack(socinfo),
socinfo_to_misc(socinfo));
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%s (%s)",
socinfo_to_soc_id(socinfo),
socinfo_to_package_id(socinfo));
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree(soc_dev_attr->revision);
kfree_const(soc_dev_attr->soc_id);
kfree(soc_dev_attr);
return PTR_ERR(soc_dev);
}
dev = soc_device_to_device(soc_dev);
dev_info(dev, "Amlogic Meson %s Revision %x:%x (%x:%x) Detected\n",
soc_dev_attr->soc_id,
socinfo_to_major(socinfo),
socinfo_to_minor(socinfo),
socinfo_to_pack(socinfo),
socinfo_to_misc(socinfo));
return 0;
}
device_initcall(meson_gx_socinfo_init);
@@ -0,0 +1,176 @@
/*
* Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#define MESON_SOCINFO_MAJOR_VER_MESON6 0x16
#define MESON_SOCINFO_MAJOR_VER_MESON8 0x19
#define MESON_SOCINFO_MAJOR_VER_MESON8B 0x1b
#define MESON_MX_ASSIST_HW_REV 0x14c
#define MESON_MX_ANALOG_TOP_METAL_REVISION 0x0
#define MESON_MX_BOOTROM_MISC_VER 0x4
static const char *meson_mx_socinfo_revision(unsigned int major_ver,
unsigned int misc_ver,
unsigned int metal_rev)
{
unsigned int minor_ver;
switch (major_ver) {
case MESON_SOCINFO_MAJOR_VER_MESON6:
minor_ver = 0xa;
break;
case MESON_SOCINFO_MAJOR_VER_MESON8:
if (metal_rev == 0x11111112)
major_ver = 0x1d;
if (metal_rev == 0x11111111 || metal_rev == 0x11111112)
minor_ver = 0xa;
else if (metal_rev == 0x11111113)
minor_ver = 0xb;
else if (metal_rev == 0x11111133)
minor_ver = 0xc;
else
minor_ver = 0xd;
break;
case MESON_SOCINFO_MAJOR_VER_MESON8B:
if (metal_rev == 0x11111111)
minor_ver = 0xa;
else
minor_ver = 0xb;
break;
default:
minor_ver = 0x0;
break;
}
return kasprintf(GFP_KERNEL, "Rev%X (%x - 0:%X)", minor_ver, major_ver,
misc_ver);
}
static const char *meson_mx_socinfo_soc_id(unsigned int major_ver,
unsigned int metal_rev)
{
const char *soc_id;
switch (major_ver) {
case MESON_SOCINFO_MAJOR_VER_MESON6:
soc_id = "Meson6 (AML8726-MX)";
break;
case MESON_SOCINFO_MAJOR_VER_MESON8:
if (metal_rev == 0x11111112)
soc_id = "Meson8m2 (S812)";
else
soc_id = "Meson8 (S802)";
break;
case MESON_SOCINFO_MAJOR_VER_MESON8B:
soc_id = "Meson8b (S805)";
break;
default:
soc_id = "Unknown";
break;
}
return kstrdup_const(soc_id, GFP_KERNEL);
}
static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = {
{ .compatible = "amlogic,meson8-analog-top", },
{ .compatible = "amlogic,meson8b-analog-top", },
{ /* sentinel */ }
};
static int __init meson_mx_socinfo_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device_node *np;
struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap;
unsigned int major_ver, misc_ver, metal_rev = 0;
int ret;
assist_regmap =
syscon_regmap_lookup_by_compatible("amlogic,meson-mx-assist");
if (IS_ERR(assist_regmap))
return PTR_ERR(assist_regmap);
bootrom_regmap =
syscon_regmap_lookup_by_compatible("amlogic,meson-mx-bootrom");
if (IS_ERR(bootrom_regmap))
return PTR_ERR(bootrom_regmap);
np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids);
if (np) {
analog_top_regmap = syscon_node_to_regmap(np);
of_node_put(np);
if (IS_ERR(analog_top_regmap))
return PTR_ERR(analog_top_regmap);
ret = regmap_read(analog_top_regmap,
MESON_MX_ANALOG_TOP_METAL_REVISION,
&metal_rev);
if (ret)
return ret;
}
ret = regmap_read(assist_regmap, MESON_MX_ASSIST_HW_REV, &major_ver);
if (ret < 0)
return ret;
ret = regmap_read(bootrom_regmap, MESON_MX_BOOTROM_MISC_VER,
&misc_ver);
if (ret < 0)
return ret;
soc_dev_attr = kzalloc_obj(*soc_dev_attr);
if (!soc_dev_attr)
return -ENODEV;
soc_dev_attr->family = "Amlogic Meson";
np = of_find_node_by_path("/");
of_property_read_string(np, "model", &soc_dev_attr->machine);
of_node_put(np);
soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver,
metal_rev);
soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev);
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree_const(soc_dev_attr->revision);
kfree_const(soc_dev_attr->soc_id);
kfree(soc_dev_attr);
return PTR_ERR(soc_dev);
}
dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n",
soc_dev_attr->soc_id, soc_dev_attr->revision);
return 0;
}
device_initcall(meson_mx_socinfo_init);