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:
Arnd Bergmann 2021-02-02 22:18:42 +01:00
commit 4fe05d21a9
9 changed files with 188 additions and 79 deletions

View File

@ -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:

View File

@ -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

View File

@ -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,
},
};

View File

@ -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

View File

@ -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[] = {

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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)