blob: 4cce5eff1b852dc7a8778d646bc00ad46ed05663 [file] [log] [blame]
/*
* Freescale LS1024A SoCs Reset Controller driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/reset-controller.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <dt-bindings/reset-controller/ls1024a-resets.h>
/**
* Reset channel regmap configuration
*
* @reset: regmap field for the channel's reset bit.
*/
struct reset_channel {
struct regmap_field *reset;
};
struct ls1024a_reset_data {
spinlock_t lock;
struct regmap *regmap;
struct reset_channel *channels;
struct reset_controller_dev rcdev;
};
#define DEVICE_RST_CNTRL 0x000
#define SERDES_RST_CNTRL 0x004
#define PCIe_SATA_RST_CNTRL 0x008
#define USB_RST_CNTRL 0x00C
#define AXI_RESET_0 0x050
#define AXI_RESET_1 0x054
#define AXI_RESET_2 0x058
#define A9DP_MPU_RESET 0x070
#define A9DP_CPU_RESET 0x078
#define A9DP_RESET 0x088
#define L2CC_RESET_CNTRL 0x098
#define TPI_RESET 0x0A8
#define CSYS_RESET 0x0B8
#define EXTPHY0_RESET 0x0C8
#define EXTPHY1_RESET 0x0D8
#define EXTPHY2_RESET 0x0E8
#define DDR_RESET 0x0F8
#define PFE_RESET 0x108
#define IPSEC_RESET 0x118
#define DECT_RESET_CNTRL 0x128
#define GEMTX_RESET_CNTRL 0x138
#define TDMNTG_RESET_CNTRL 0x148
#define TSUNTG_RESET 0x158
#define SATA_PMU_RESET_CNTRL 0x168
#define SATA_OOB_RESET_CNTRL 0x178
#define SATA_OCC_RESET 0x188
#define PCIE_OCC_RESET 0x198
#define SGMII_OCC_RESET 0x1A8
#define RESET_BIT(_rr, _rb) REG_FIELD((_rr), (_rb), (_rb))
static const struct reg_field reset_bits[] = {
[LS1024A_AXI_DPI_CIE_RESET] = RESET_BIT(AXI_RESET_0, 5),
[LS1024A_AXI_DPI_DECOMP_RESET] = RESET_BIT(AXI_RESET_0, 6),
[LS1024A_AXI_IPSEC_EAPE_RESET] = RESET_BIT(AXI_RESET_1, 1),
[LS1024A_AXI_IPSEC_SPACC_RESET] = RESET_BIT(AXI_RESET_1, 2),
[LS1024A_PFE_SYS_RESET] = RESET_BIT(AXI_RESET_1, 3),
[LS1024A_AXI_TDM_RESET] = RESET_BIT(AXI_RESET_1, 4),
[LS1024A_AXI_RTC_RESET] = RESET_BIT(AXI_RESET_1, 7),
[LS1024A_AXI_PCIE0_RESET] = RESET_BIT(AXI_RESET_2, 0),
[LS1024A_AXI_PCIE1_RESET] = RESET_BIT(AXI_RESET_2, 1),
[LS1024A_AXI_SATA_RESET] = RESET_BIT(AXI_RESET_2, 2),
[LS1024A_AXI_USB0_RESET] = RESET_BIT(AXI_RESET_2, 3),
[LS1024A_AXI_USB1_RESET] = RESET_BIT(AXI_RESET_2, 4),
[LS1024A_USB0_PHY_RESET] = RESET_BIT(USB_RST_CNTRL, 0),
[LS1024A_UTMI_USB0_RESET] = RESET_BIT(USB_RST_CNTRL, 1),
[LS1024A_USB1_PHY_RESET] = RESET_BIT(USB_RST_CNTRL, 4),
[LS1024A_UTMI_USB1_RESET] = RESET_BIT(USB_RST_CNTRL, 5),
[LS1024A_SERDES_PCIE0_RESET] = REG_FIELD(PCIe_SATA_RST_CNTRL, 0, 1),
[LS1024A_SERDES_PCIE1_RESET] = REG_FIELD(PCIe_SATA_RST_CNTRL, 2, 3),
[LS1024A_SERDES_SATA0_RESET] = REG_FIELD(PCIe_SATA_RST_CNTRL, 4, 5),
[LS1024A_SERDES_SATA1_RESET] = REG_FIELD(PCIe_SATA_RST_CNTRL, 6, 7),
[LS1024A_PFE_CORE_RESET] = RESET_BIT(PFE_RESET, 0),
[LS1024A_IPSEC_EAPE_CORE_RESET] = RESET_BIT(IPSEC_RESET, 0),
[LS1024A_GEMTX_RESET] = RESET_BIT(GEMTX_RESET_CNTRL, 0),
[LS1024A_DECT_RESET] = RESET_BIT(DECT_RESET_CNTRL, 0),
[LS1024A_DDR_CNTLR_RESET] = RESET_BIT(DDR_RESET, 1),
[LS1024A_DDR_PHY_RESET] = RESET_BIT(DDR_RESET, 0),
[LS1024A_SERDES0_RESET] = RESET_BIT(SERDES_RST_CNTRL, 0),
[LS1024A_SERDES1_RESET] = RESET_BIT(SERDES_RST_CNTRL, 1),
[LS1024A_SERDES2_RESET] = RESET_BIT(SERDES_RST_CNTRL, 2),
[LS1024A_SGMII_RESET] = RESET_BIT(SGMII_OCC_RESET, 0),
[LS1024A_SATA_PMU_RESET] = RESET_BIT(SATA_PMU_RESET_CNTRL, 0),
[LS1024A_SATA_OOB_RESET] = RESET_BIT(SATA_OOB_RESET_CNTRL, 0),
[LS1024A_TDMNTG_RESET] = RESET_BIT(TDMNTG_RESET_CNTRL, 0),
};
#define to_ls1024a_reset_data(_rst) \
container_of(_rst, struct ls1024a_reset_data, rcdev)
static int ls1024a_reset_program_hw(struct reset_controller_dev *rcdev,
unsigned long idx, int assert)
{
struct ls1024a_reset_data *rst = to_ls1024a_reset_data(rcdev);
const struct reset_channel *ch;
u32 ctrl_val = assert ? 0xFFFFFFFF : 0;
int err;
if (idx >= rcdev->nr_resets)
return -EINVAL;
ch = &rst->channels[idx];
err = regmap_field_write(ch->reset, ctrl_val);
if (err)
return err;
return 0;
}
static int ls1024a_reset_assert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
return ls1024a_reset_program_hw(rcdev, idx, true);
}
static int ls1024a_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
return ls1024a_reset_program_hw(rcdev, idx, false);
}
static int ls1024a_reset_status(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct ls1024a_reset_data *rst = to_ls1024a_reset_data(rcdev);
const struct reset_channel *ch;
unsigned cur_val;
int err;
if (idx >= rcdev->nr_resets)
return -EINVAL;
ch = &rst->channels[idx];
err = regmap_field_read(ch->reset, &cur_val);
if (err)
return err;
return cur_val;
}
static struct reset_control_ops ls1024a_reset_ops = {
.assert = ls1024a_reset_assert,
.deassert = ls1024a_reset_deassert,
.status = ls1024a_reset_status,
};
static int ls1024a_reset_probe(struct platform_device *pdev)
{
struct ls1024a_reset_data *data;
size_t size;
int i;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
size = sizeof(struct reset_channel) * ARRAY_SIZE(reset_bits);
data->channels = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (!data->channels)
return -ENOMEM;
data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "syscon");
if (IS_ERR(data->regmap)) {
dev_err(&pdev->dev,
"Error retrieving reset syscon\n");
return PTR_ERR(data->regmap);
}
spin_lock_init(&data->lock);
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = ARRAY_SIZE(reset_bits);
data->rcdev.ops = &ls1024a_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
for (i = 0; i < ARRAY_SIZE(reset_bits); i++) {
struct regmap_field *f;
const struct reg_field zero = {0};
if (memcmp(&reset_bits[i], &zero, sizeof(zero))) {
f = devm_regmap_field_alloc(&pdev->dev, data->regmap, reset_bits[i]);
if (IS_ERR(f))
return PTR_ERR(f);
data->channels[i].reset = f;
}
}
return reset_controller_register(&data->rcdev);
}
static const struct of_device_id ls1024a_reset_dt_ids[] = {
{ .compatible = "fsl,ls1024a-reset", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, ls1024a_reset_dt_ids);
static struct platform_driver ls1024a_reset_driver = {
.probe = ls1024a_reset_probe,
.driver = {
.name = "ls1024a-reset",
.of_match_table = ls1024a_reset_dt_ids,
},
};
static int __init ls1024a_reset_init(void)
{
return platform_driver_register(&ls1024a_reset_driver);
}
arch_initcall(ls1024a_reset_init);
MODULE_DESCRIPTION("Freescale LS1024A SoCs Reset Controller Driver");
MODULE_LICENSE("GPL");