mirror of https://gitee.com/openkylin/linux.git
Some new drivers changes for the Allwinner SoCs, but mostly runtime_pm and
suspend / resume support for our RSB bus, and support for the H616 -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCYBbvvwAKCRDj7w1vZxhR xR+qAP47qmg1qUkp4I91zWoK1h2iLme3zusGI/8hRp7la5ldxQD/dEShzw9OsTBh cuCmbtaPXCLJw/GRiRNnBdUv/+JQRww= =F2ck -----END PGP SIGNATURE----- Merge tag 'sunxi-drivers-for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into arm/drivers Some new drivers changes for the Allwinner SoCs, but mostly runtime_pm and suspend / resume support for our RSB bus, and support for the H616 * tag 'sunxi-drivers-for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux: dt-bindings: bus: rsb: Add H616 compatible string soc: sunxi: sram: Add support for more than one EMAC clock dt-bindings: sram: sunxi-sram: Add H616 compatible string mfd/bus: sunxi-rsb: Make .remove() callback return void bus: sunxi-rsb: Implement runtime power management bus: sunxi-rsb: Implement suspend/resume/shutdown callbacks bus: sunxi-rsb: Split out controller init/exit functions bus: sunxi-rsb: Move OF match table Link: https://lore.kernel.org/r/91f2980f-266f-41f2-ba10-5a395625498c.lettre@localhost Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
4fe05d21a9
|
@ -21,7 +21,9 @@ properties:
|
|||
oneOf:
|
||||
- const: allwinner,sun8i-a23-rsb
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-rsb
|
||||
- enum:
|
||||
- allwinner,sun8i-a83t-rsb
|
||||
- allwinner,sun50i-h616-rsb
|
||||
- const: allwinner,sun8i-a23-rsb
|
||||
|
||||
reg:
|
||||
|
|
|
@ -49,6 +49,7 @@ properties:
|
|||
- items:
|
||||
- const: allwinner,suniv-f1c100s-system-control
|
||||
- const: allwinner,sun4i-a10-system-control
|
||||
- const: allwinner,sun50i-h616-system-control
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -126,6 +128,7 @@ struct sunxi_rsb {
|
|||
struct completion complete;
|
||||
struct mutex lock;
|
||||
unsigned int status;
|
||||
u32 clk_freq;
|
||||
};
|
||||
|
||||
/* bus / slave device related functions */
|
||||
|
@ -170,7 +173,9 @@ static int sunxi_rsb_device_remove(struct device *dev)
|
|||
{
|
||||
const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver);
|
||||
|
||||
return drv->remove(to_sunxi_rsb_device(dev));
|
||||
drv->remove(to_sunxi_rsb_device(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bus_type sunxi_rsb_bus = {
|
||||
|
@ -335,6 +340,10 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = pm_runtime_resume_and_get(rsb->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&rsb->lock);
|
||||
|
||||
writel(addr, rsb->regs + RSB_ADDR);
|
||||
|
@ -350,6 +359,9 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
|
|||
unlock:
|
||||
mutex_unlock(&rsb->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(rsb->dev);
|
||||
pm_runtime_put_autosuspend(rsb->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -377,6 +389,10 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = pm_runtime_resume_and_get(rsb->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&rsb->lock);
|
||||
|
||||
writel(addr, rsb->regs + RSB_ADDR);
|
||||
|
@ -387,6 +403,9 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
|
|||
|
||||
mutex_unlock(&rsb->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(rsb->dev);
|
||||
pm_runtime_put_autosuspend(rsb->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -614,11 +633,100 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sunxi_rsb_of_match_table[] = {
|
||||
{ .compatible = "allwinner,sun8i-a23-rsb" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
|
||||
static int sunxi_rsb_hw_init(struct sunxi_rsb *rsb)
|
||||
{
|
||||
struct device *dev = rsb->dev;
|
||||
unsigned long p_clk_freq;
|
||||
u32 clk_delay, reg;
|
||||
int clk_div, ret;
|
||||
|
||||
ret = clk_prepare_enable(rsb->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(rsb->rstc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to deassert reset line: %d\n", ret);
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
/* reset the controller */
|
||||
writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
|
||||
readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
|
||||
!(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
|
||||
|
||||
/*
|
||||
* Clock frequency and delay calculation code is from
|
||||
* Allwinner U-boot sources.
|
||||
*
|
||||
* From A83 user manual:
|
||||
* bus clock frequency = parent clock frequency / (2 * (divider + 1))
|
||||
*/
|
||||
p_clk_freq = clk_get_rate(rsb->clk);
|
||||
clk_div = p_clk_freq / rsb->clk_freq / 2;
|
||||
if (!clk_div)
|
||||
clk_div = 1;
|
||||
else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
|
||||
clk_div = RSB_CCR_MAX_CLK_DIV + 1;
|
||||
|
||||
clk_delay = clk_div >> 1;
|
||||
if (!clk_delay)
|
||||
clk_delay = 1;
|
||||
|
||||
dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
|
||||
writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
|
||||
rsb->regs + RSB_CCR);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(rsb->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sunxi_rsb_hw_exit(struct sunxi_rsb *rsb)
|
||||
{
|
||||
/* Keep the clock and PM reference counts consistent. */
|
||||
if (pm_runtime_status_suspended(rsb->dev))
|
||||
pm_runtime_resume(rsb->dev);
|
||||
reset_control_assert(rsb->rstc);
|
||||
clk_disable_unprepare(rsb->clk);
|
||||
}
|
||||
|
||||
static int __maybe_unused sunxi_rsb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sunxi_rsb *rsb = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(rsb->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sunxi_rsb_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sunxi_rsb *rsb = dev_get_drvdata(dev);
|
||||
|
||||
return clk_prepare_enable(rsb->clk);
|
||||
}
|
||||
|
||||
static int __maybe_unused sunxi_rsb_suspend(struct device *dev)
|
||||
{
|
||||
struct sunxi_rsb *rsb = dev_get_drvdata(dev);
|
||||
|
||||
sunxi_rsb_hw_exit(rsb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sunxi_rsb_resume(struct device *dev)
|
||||
{
|
||||
struct sunxi_rsb *rsb = dev_get_drvdata(dev);
|
||||
|
||||
return sunxi_rsb_hw_init(rsb);
|
||||
}
|
||||
|
||||
static int sunxi_rsb_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -626,10 +734,8 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
|||
struct device_node *np = dev->of_node;
|
||||
struct resource *r;
|
||||
struct sunxi_rsb *rsb;
|
||||
unsigned long p_clk_freq;
|
||||
u32 clk_delay, clk_freq = 3000000;
|
||||
int clk_div, irq, ret;
|
||||
u32 reg;
|
||||
u32 clk_freq = 3000000;
|
||||
int irq, ret;
|
||||
|
||||
of_property_read_u32(np, "clock-frequency", &clk_freq);
|
||||
if (clk_freq > RSB_MAX_FREQ) {
|
||||
|
@ -644,6 +750,7 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
rsb->dev = dev;
|
||||
rsb->clk_freq = clk_freq;
|
||||
platform_set_drvdata(pdev, rsb);
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rsb->regs = devm_ioremap_resource(dev, r);
|
||||
|
@ -661,79 +768,41 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(rsb->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
p_clk_freq = clk_get_rate(rsb->clk);
|
||||
|
||||
rsb->rstc = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(rsb->rstc)) {
|
||||
ret = PTR_ERR(rsb->rstc);
|
||||
dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(rsb->rstc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to deassert reset line: %d\n", ret);
|
||||
goto err_clk_disable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&rsb->complete);
|
||||
mutex_init(&rsb->lock);
|
||||
|
||||
/* reset the controller */
|
||||
writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
|
||||
readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
|
||||
!(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
|
||||
|
||||
/*
|
||||
* Clock frequency and delay calculation code is from
|
||||
* Allwinner U-boot sources.
|
||||
*
|
||||
* From A83 user manual:
|
||||
* bus clock frequency = parent clock frequency / (2 * (divider + 1))
|
||||
*/
|
||||
clk_div = p_clk_freq / clk_freq / 2;
|
||||
if (!clk_div)
|
||||
clk_div = 1;
|
||||
else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
|
||||
clk_div = RSB_CCR_MAX_CLK_DIV + 1;
|
||||
|
||||
clk_delay = clk_div >> 1;
|
||||
if (!clk_delay)
|
||||
clk_delay = 1;
|
||||
|
||||
dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
|
||||
writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
|
||||
rsb->regs + RSB_CCR);
|
||||
|
||||
ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't register interrupt handler irq %d: %d\n",
|
||||
irq, ret);
|
||||
goto err_reset_assert;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sunxi_rsb_hw_init(rsb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* initialize all devices on the bus into RSB mode */
|
||||
ret = sunxi_rsb_init_device_mode(rsb);
|
||||
if (ret)
|
||||
dev_warn(dev, "Initialize device mode failed: %d\n", ret);
|
||||
|
||||
pm_suspend_ignore_children(dev, true);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
of_rsb_register_devices(rsb);
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset_assert:
|
||||
reset_control_assert(rsb->rstc);
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(rsb->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sunxi_rsb_remove(struct platform_device *pdev)
|
||||
|
@ -741,18 +810,40 @@ static int sunxi_rsb_remove(struct platform_device *pdev)
|
|||
struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
|
||||
|
||||
device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
|
||||
reset_control_assert(rsb->rstc);
|
||||
clk_disable_unprepare(rsb->clk);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
sunxi_rsb_hw_exit(rsb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_rsb_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
sunxi_rsb_hw_exit(rsb);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sunxi_rsb_runtime_suspend,
|
||||
sunxi_rsb_runtime_resume, NULL)
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_rsb_of_match_table[] = {
|
||||
{ .compatible = "allwinner,sun8i-a23-rsb" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
|
||||
|
||||
static struct platform_driver sunxi_rsb_driver = {
|
||||
.probe = sunxi_rsb_probe,
|
||||
.remove = sunxi_rsb_remove,
|
||||
.shutdown = sunxi_rsb_shutdown,
|
||||
.driver = {
|
||||
.name = RSB_CTRL_NAME,
|
||||
.of_match_table = sunxi_rsb_of_match_table,
|
||||
.pm = &sunxi_rsb_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -54,7 +54,9 @@ static int axp20x_i2c_remove(struct i2c_client *i2c)
|
|||
{
|
||||
struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
|
||||
|
||||
return axp20x_device_remove(axp20x);
|
||||
axp20x_device_remove(axp20x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -49,11 +49,11 @@ static int axp20x_rsb_probe(struct sunxi_rsb_device *rdev)
|
|||
return axp20x_device_probe(axp20x);
|
||||
}
|
||||
|
||||
static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
|
||||
static void axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
|
||||
{
|
||||
struct axp20x_dev *axp20x = sunxi_rsb_device_get_drvdata(rdev);
|
||||
|
||||
return axp20x_device_remove(axp20x);
|
||||
axp20x_device_remove(axp20x);
|
||||
}
|
||||
|
||||
static const struct of_device_id axp20x_rsb_of_match[] = {
|
||||
|
|
|
@ -987,7 +987,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
|
|||
}
|
||||
EXPORT_SYMBOL(axp20x_device_probe);
|
||||
|
||||
int axp20x_device_remove(struct axp20x_dev *axp20x)
|
||||
void axp20x_device_remove(struct axp20x_dev *axp20x)
|
||||
{
|
||||
if (axp20x == axp20x_pm_power_off) {
|
||||
axp20x_pm_power_off = NULL;
|
||||
|
@ -996,8 +996,6 @@ int axp20x_device_remove(struct axp20x_dev *axp20x)
|
|||
|
||||
mfd_remove_devices(axp20x->dev);
|
||||
regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(axp20x_device_remove);
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ int sunxi_sram_release(struct device *dev)
|
|||
EXPORT_SYMBOL(sunxi_sram_release);
|
||||
|
||||
struct sunxi_sramc_variant {
|
||||
bool has_emac_clock;
|
||||
int num_emac_clocks;
|
||||
};
|
||||
|
||||
static const struct sunxi_sramc_variant sun4i_a10_sramc_variant = {
|
||||
|
@ -291,20 +291,31 @@ static const struct sunxi_sramc_variant sun4i_a10_sramc_variant = {
|
|||
};
|
||||
|
||||
static const struct sunxi_sramc_variant sun8i_h3_sramc_variant = {
|
||||
.has_emac_clock = true,
|
||||
.num_emac_clocks = 1,
|
||||
};
|
||||
|
||||
static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = {
|
||||
.has_emac_clock = true,
|
||||
.num_emac_clocks = 1,
|
||||
};
|
||||
|
||||
static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = {
|
||||
.num_emac_clocks = 2,
|
||||
};
|
||||
|
||||
#define SUNXI_SRAM_EMAC_CLOCK_REG 0x30
|
||||
static bool sunxi_sram_regmap_accessible_reg(struct device *dev,
|
||||
unsigned int reg)
|
||||
{
|
||||
if (reg == SUNXI_SRAM_EMAC_CLOCK_REG)
|
||||
return true;
|
||||
return false;
|
||||
const struct sunxi_sramc_variant *variant;
|
||||
|
||||
variant = of_device_get_match_data(dev);
|
||||
|
||||
if (reg < SUNXI_SRAM_EMAC_CLOCK_REG)
|
||||
return false;
|
||||
if (reg > SUNXI_SRAM_EMAC_CLOCK_REG + variant->num_emac_clocks * 4)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct regmap_config sunxi_sram_emac_clock_regmap = {
|
||||
|
@ -312,7 +323,7 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = {
|
|||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
/* last defined register */
|
||||
.max_register = SUNXI_SRAM_EMAC_CLOCK_REG,
|
||||
.max_register = SUNXI_SRAM_EMAC_CLOCK_REG + 4,
|
||||
/* other devices have no business accessing other registers */
|
||||
.readable_reg = sunxi_sram_regmap_accessible_reg,
|
||||
.writeable_reg = sunxi_sram_regmap_accessible_reg,
|
||||
|
@ -343,7 +354,7 @@ static int sunxi_sram_probe(struct platform_device *pdev)
|
|||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
if (variant->has_emac_clock) {
|
||||
if (variant->num_emac_clocks > 0) {
|
||||
emac_clock = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&sunxi_sram_emac_clock_regmap);
|
||||
|
||||
|
@ -387,6 +398,10 @@ static const struct of_device_id sunxi_sram_dt_match[] = {
|
|||
.compatible = "allwinner,sun50i-h5-system-control",
|
||||
.data = &sun50i_a64_sramc_variant,
|
||||
},
|
||||
{
|
||||
.compatible = "allwinner,sun50i-h616-system-control",
|
||||
.data = &sun50i_h616_sramc_variant,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match);
|
||||
|
|
|
@ -696,6 +696,6 @@ int axp20x_device_probe(struct axp20x_dev *axp20x);
|
|||
*
|
||||
* This tells the axp20x core to remove the associated mfd devices
|
||||
*/
|
||||
int axp20x_device_remove(struct axp20x_dev *axp20x);
|
||||
void axp20x_device_remove(struct axp20x_dev *axp20x);
|
||||
|
||||
#endif /* __LINUX_MFD_AXP20X_H */
|
||||
|
|
|
@ -59,7 +59,7 @@ static inline void sunxi_rsb_device_set_drvdata(struct sunxi_rsb_device *rdev,
|
|||
struct sunxi_rsb_driver {
|
||||
struct device_driver driver;
|
||||
int (*probe)(struct sunxi_rsb_device *rdev);
|
||||
int (*remove)(struct sunxi_rsb_device *rdev);
|
||||
void (*remove)(struct sunxi_rsb_device *rdev);
|
||||
};
|
||||
|
||||
static inline struct sunxi_rsb_driver *to_sunxi_rsb_driver(struct device_driver *d)
|
||||
|
|
Loading…
Reference in New Issue