ti-sysc interconnect target module driver changes for v5.3

This series of changes improves probing devices with ti-sysc to the
 point where we can now probe most devices without the custom dts
 property "ti,hwmods" and no legacy platform data :)
 
 We add support for platform data callbacks for idling and unidling the
 clockdomain the module belongs to. The rest of the series mostly adds
 handling for the various quirks needed by old legacy modules such as
 i2c and watchdog. Some quirk handling is still missing for few modules,
 but those will be added as they get tested.
 
 The related platform data and dts changes will be sent separately.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEkgNvrZJU/QSQYIcQG9Q+yVyrpXMFAl0AuTURHHRvbnlAYXRv
 bWlkZS5jb20ACgkQG9Q+yVyrpXPn4A//Z7uWk98dwcIs8/28CaFTPsRGhx3p+hj0
 Mafl3eT5Q/y0TFGkMw3g5LSX5EZlz+5t6eFsolU+O8p6jsPoo5mcsGQfgkraydE3
 x0wfSkFiqJcTP4FFvrwcekSQZGjpeZoUdQZ/e2epSDWvPx9gRzLFR3wCLPJ1MOgg
 dveEJTnPfpFLliTZeVpcn4v2P4+KfBn4iu2ojr9qcLZL9QNFZr3NQLRdx6kUw7qf
 b3eRugzx3sBA4pKLEfqrOzqjP77W9sy1zDN5rA3GNgo+b+OyBhdu/pxxkREP4t4+
 8/1E/JndDVkO98ooFJ+MWdnh8HQmefEjyaoaYa7LN6438mivkOFPFAUMtuOdKzv/
 FTa9r7xpo1Iq5b3HyE7NA+xZfoHKBEHzY0zbNyJLz75Ctnh3zOuHAA3xwxOlbgn+
 UX/1DAS/7ALUXrXkk331VVzYX5UfOxrH2+CF2GF9vpCNZZ17Qp3ZKy4XW1Qv9nfy
 nozOkqSvbGG0tzhq+wLd6Soo2Vns7CfHRqDB8txLeBGtrk/kahHsxe7dLo4+fHk6
 DH0R7mZ6QnvaMWpJwkqoBUp7lD9ej9/pw2newbb5RyUDQqMjNvdY3zcBOeXeWulh
 m2hWFKt1JTGTvXtbTjOOnfQIyICUClMzo4VowLBplfZELJvK781/5P3Jb4ZOFF0N
 I1DN4vDxqMg=
 =Fxul
 -----END PGP SIGNATURE-----

Merge tag 'omap-for-v5.3/ti-sysc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into arm/drivers

ti-sysc interconnect target module driver changes for v5.3

This series of changes improves probing devices with ti-sysc to the
point where we can now probe most devices without the custom dts
property "ti,hwmods" and no legacy platform data :)

We add support for platform data callbacks for idling and unidling the
clockdomain the module belongs to. The rest of the series mostly adds
handling for the various quirks needed by old legacy modules such as
i2c and watchdog. Some quirk handling is still missing for few modules,
but those will be added as they get tested.

The related platform data and dts changes will be sent separately.

* tag 'omap-for-v5.3/ti-sysc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
  bus: ti-sysc: Add support for module specific reset quirks
  bus: ti-sysc: Detect uarts also on omap34xx
  bus: ti-sysc: Do rstctrl reset handling in two phases
  bus: ti-sysc: Add support for disabling module without legacy mode
  bus: ti-sysc: Set ENAWAKEUP if available
  bus: ti-sysc: Handle swsup idle mode quirks
  bus: ti-sysc: Handle clockactivity for enable and disable
  bus: ti-sysc: Enable interconnect target module autoidle bit on enable
  bus: ti-sysc: Allow QUIRK_LEGACY_IDLE even if legacy_mode is not set
  bus: ti-sysc: Make OCP reset work for sysstatus and sysconfig reset bits
  bus: ti-sysc: Support 16-bit writes too
  bus: ti-sysc: Add support for missing clockdomain handling
  ARM: dts: dra71x: Disable usb4_tm target module
  ARM: dts: dra71x: Disable rtc target module
  ARM: dts: dra76x: Disable usb4_tm target module
  ARM: dts: dra76x: Disable rtc target module
  ARM: dts: dra76x: Update MMC2_HS200_MANUAL1 iodelay values
  ARM: dts: am57xx-idk: Remove support for voltage switching for SD card
  bus: ti-sysc: Handle devices with no control registers
  ARM: dts: Configure osc clock for d_can on am335x

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2019-06-17 04:53:32 -07:00
commit c3bd15a078
12 changed files with 520 additions and 154 deletions

View File

@ -1759,11 +1759,10 @@
target-module@cc000 { /* 0x481cc000, ap 60 46.0 */ target-module@cc000 { /* 0x481cc000, ap 60 46.0 */
compatible = "ti,sysc-omap4", "ti,sysc"; compatible = "ti,sysc-omap4", "ti,sysc";
ti,hwmods = "d_can0"; ti,hwmods = "d_can0";
reg = <0xcc000 0x4>;
reg-names = "rev";
/* Domains (P, C): per_pwrdm, l4ls_clkdm */ /* Domains (P, C): per_pwrdm, l4ls_clkdm */
clocks = <&l4ls_clkctrl AM3_L4LS_D_CAN0_CLKCTRL 0>; clocks = <&l4ls_clkctrl AM3_L4LS_D_CAN0_CLKCTRL 0>,
clock-names = "fck"; <&dcan0_fck>;
clock-names = "fck", "osc";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;
ranges = <0x0 0xcc000 0x2000>; ranges = <0x0 0xcc000 0x2000>;
@ -1782,11 +1781,10 @@
target-module@d0000 { /* 0x481d0000, ap 62 42.0 */ target-module@d0000 { /* 0x481d0000, ap 62 42.0 */
compatible = "ti,sysc-omap4", "ti,sysc"; compatible = "ti,sysc-omap4", "ti,sysc";
ti,hwmods = "d_can1"; ti,hwmods = "d_can1";
reg = <0xd0000 0x4>;
reg-names = "rev";
/* Domains (P, C): per_pwrdm, l4ls_clkdm */ /* Domains (P, C): per_pwrdm, l4ls_clkdm */
clocks = <&l4ls_clkctrl AM3_L4LS_D_CAN1_CLKCTRL 0>; clocks = <&l4ls_clkctrl AM3_L4LS_D_CAN1_CLKCTRL 0>,
clock-names = "fck"; <&dcan1_fck>;
clock-names = "fck", "osc";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;
ranges = <0x0 0xd0000 0x2000>; ranges = <0x0 0xd0000 0x2000>;

View File

@ -1575,8 +1575,6 @@
target-module@cc000 { /* 0x481cc000, ap 50 46.0 */ target-module@cc000 { /* 0x481cc000, ap 50 46.0 */
compatible = "ti,sysc-omap4", "ti,sysc"; compatible = "ti,sysc-omap4", "ti,sysc";
ti,hwmods = "d_can0"; ti,hwmods = "d_can0";
reg = <0xcc000 0x4>;
reg-names = "rev";
/* Domains (P, C): per_pwrdm, l4ls_clkdm */ /* Domains (P, C): per_pwrdm, l4ls_clkdm */
clocks = <&l4ls_clkctrl AM4_L4LS_D_CAN0_CLKCTRL 0>; clocks = <&l4ls_clkctrl AM4_L4LS_D_CAN0_CLKCTRL 0>;
clock-names = "fck"; clock-names = "fck";
@ -1596,8 +1594,6 @@
target-module@d0000 { /* 0x481d0000, ap 52 3a.0 */ target-module@d0000 { /* 0x481d0000, ap 52 3a.0 */
compatible = "ti,sysc-omap4", "ti,sysc"; compatible = "ti,sysc-omap4", "ti,sysc";
ti,hwmods = "d_can1"; ti,hwmods = "d_can1";
reg = <0xd0000 0x4>;
reg-names = "rev";
/* Domains (P, C): per_pwrdm, l4ls_clkdm */ /* Domains (P, C): per_pwrdm, l4ls_clkdm */
clocks = <&l4ls_clkctrl AM4_L4LS_D_CAN1_CLKCTRL 0>; clocks = <&l4ls_clkctrl AM4_L4LS_D_CAN1_CLKCTRL 0>;
clock-names = "fck"; clock-names = "fck";

View File

@ -420,6 +420,7 @@
vqmmc-supply = <&ldo1_reg>; vqmmc-supply = <&ldo1_reg>;
bus-width = <4>; bus-width = <4>;
cd-gpios = <&gpio6 27 GPIO_ACTIVE_LOW>; /* gpio 219 */ cd-gpios = <&gpio6 27 GPIO_ACTIVE_LOW>; /* gpio 219 */
no-1-8-v;
}; };
&mmc2 { &mmc2 {

View File

@ -3543,7 +3543,7 @@
}; };
}; };
target-module@38000 { /* 0x48838000, ap 29 12.0 */ rtctarget: target-module@38000 { /* 0x48838000, ap 29 12.0 */
compatible = "ti,sysc-omap4-simple", "ti,sysc"; compatible = "ti,sysc-omap4-simple", "ti,sysc";
ti,hwmods = "rtcss"; ti,hwmods = "rtcss";
reg = <0x38074 0x4>, reg = <0x38074 0x4>,

View File

@ -6,7 +6,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include "dra72-evm-common.dtsi" #include "dra71x.dtsi"
#include "dra7-mmc-iodelay.dtsi" #include "dra7-mmc-iodelay.dtsi"
#include "dra72x-mmc-iodelay.dtsi" #include "dra72x-mmc-iodelay.dtsi"
#include <dt-bindings/net/ti-dp83867.h> #include <dt-bindings/net/ti-dp83867.h>

View File

@ -0,0 +1,17 @@
/*
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "dra72-evm-common.dtsi"
&rtctarget {
status = "disabled";
};
&usb4_tm {
status = "disabled";
};

View File

@ -22,7 +22,7 @@
* *
* Datamanual Revisions: * Datamanual Revisions:
* *
* DRA76x Silicon Revision 1.0: SPRS993A, Revised July 2017 * DRA76x Silicon Revision 1.0: SPRS993E, Revised December 2018
* *
*/ */
@ -169,25 +169,25 @@
/* Corresponds to MMC2_HS200_MANUAL1 in datamanual */ /* Corresponds to MMC2_HS200_MANUAL1 in datamanual */
mmc2_iodelay_hs200_conf: mmc2_iodelay_hs200_conf { mmc2_iodelay_hs200_conf: mmc2_iodelay_hs200_conf {
pinctrl-pin-array = < pinctrl-pin-array = <
0x190 A_DELAY_PS(384) G_DELAY_PS(0) /* CFG_GPMC_A19_OEN */ 0x190 A_DELAY_PS(384) G_DELAY_PS(0) /* CFG_GPMC_A19_OEN */
0x194 A_DELAY_PS(0) G_DELAY_PS(174) /* CFG_GPMC_A19_OUT */ 0x194 A_DELAY_PS(350) G_DELAY_PS(174) /* CFG_GPMC_A19_OUT */
0x1a8 A_DELAY_PS(410) G_DELAY_PS(0) /* CFG_GPMC_A20_OEN */ 0x1a8 A_DELAY_PS(410) G_DELAY_PS(0) /* CFG_GPMC_A20_OEN */
0x1ac A_DELAY_PS(85) G_DELAY_PS(0) /* CFG_GPMC_A20_OUT */ 0x1ac A_DELAY_PS(335) G_DELAY_PS(0) /* CFG_GPMC_A20_OUT */
0x1b4 A_DELAY_PS(468) G_DELAY_PS(0) /* CFG_GPMC_A21_OEN */ 0x1b4 A_DELAY_PS(468) G_DELAY_PS(0) /* CFG_GPMC_A21_OEN */
0x1b8 A_DELAY_PS(139) G_DELAY_PS(0) /* CFG_GPMC_A21_OUT */ 0x1b8 A_DELAY_PS(339) G_DELAY_PS(0) /* CFG_GPMC_A21_OUT */
0x1c0 A_DELAY_PS(676) G_DELAY_PS(0) /* CFG_GPMC_A22_OEN */ 0x1c0 A_DELAY_PS(676) G_DELAY_PS(0) /* CFG_GPMC_A22_OEN */
0x1c4 A_DELAY_PS(69) G_DELAY_PS(0) /* CFG_GPMC_A22_OUT */ 0x1c4 A_DELAY_PS(219) G_DELAY_PS(0) /* CFG_GPMC_A22_OUT */
0x1d0 A_DELAY_PS(1062) G_DELAY_PS(154) /* CFG_GPMC_A23_OUT */ 0x1d0 A_DELAY_PS(1062) G_DELAY_PS(154) /* CFG_GPMC_A23_OUT */
0x1d8 A_DELAY_PS(640) G_DELAY_PS(0) /* CFG_GPMC_A24_OEN */ 0x1d8 A_DELAY_PS(640) G_DELAY_PS(0) /* CFG_GPMC_A24_OEN */
0x1dc A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A24_OUT */ 0x1dc A_DELAY_PS(150) G_DELAY_PS(0) /* CFG_GPMC_A24_OUT */
0x1e4 A_DELAY_PS(356) G_DELAY_PS(0) /* CFG_GPMC_A25_OEN */ 0x1e4 A_DELAY_PS(356) G_DELAY_PS(0) /* CFG_GPMC_A25_OEN */
0x1e8 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A25_OUT */ 0x1e8 A_DELAY_PS(150) G_DELAY_PS(0) /* CFG_GPMC_A25_OUT */
0x1f0 A_DELAY_PS(579) G_DELAY_PS(0) /* CFG_GPMC_A26_OEN */ 0x1f0 A_DELAY_PS(579) G_DELAY_PS(0) /* CFG_GPMC_A26_OEN */
0x1f4 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A26_OUT */ 0x1f4 A_DELAY_PS(200) G_DELAY_PS(0) /* CFG_GPMC_A26_OUT */
0x1fc A_DELAY_PS(435) G_DELAY_PS(0) /* CFG_GPMC_A27_OEN */ 0x1fc A_DELAY_PS(435) G_DELAY_PS(0) /* CFG_GPMC_A27_OEN */
0x200 A_DELAY_PS(36) G_DELAY_PS(0) /* CFG_GPMC_A27_OUT */ 0x200 A_DELAY_PS(236) G_DELAY_PS(0) /* CFG_GPMC_A27_OUT */
0x364 A_DELAY_PS(759) G_DELAY_PS(0) /* CFG_GPMC_CS1_OEN */ 0x364 A_DELAY_PS(759) G_DELAY_PS(0) /* CFG_GPMC_CS1_OEN */
0x368 A_DELAY_PS(72) G_DELAY_PS(0) /* CFG_GPMC_CS1_OUT */ 0x368 A_DELAY_PS(372) G_DELAY_PS(0) /* CFG_GPMC_CS1_OUT */
>; >;
}; };

View File

@ -81,3 +81,11 @@
reg = <0x3fc>; reg = <0x3fc>;
}; };
}; };
&rtctarget {
status = "disabled";
};
&usb4_tm {
status = "disabled";
};

View File

@ -3445,6 +3445,7 @@ static int omap_hwmod_check_module(struct device *dev,
* @dev: struct device * @dev: struct device
* @oh: module * @oh: module
* @sysc_fields: sysc register bits * @sysc_fields: sysc register bits
* @clockdomain: clockdomain
* @rev_offs: revision register offset * @rev_offs: revision register offset
* @sysc_offs: sysconfig register offset * @sysc_offs: sysconfig register offset
* @syss_offs: sysstatus register offset * @syss_offs: sysstatus register offset
@ -3456,6 +3457,7 @@ static int omap_hwmod_check_module(struct device *dev,
static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh,
const struct ti_sysc_module_data *data, const struct ti_sysc_module_data *data,
struct sysc_regbits *sysc_fields, struct sysc_regbits *sysc_fields,
struct clockdomain *clkdm,
s32 rev_offs, s32 sysc_offs, s32 rev_offs, s32 sysc_offs,
s32 syss_offs, u32 sysc_flags, s32 syss_offs, u32 sysc_flags,
u32 idlemodes) u32 idlemodes)
@ -3463,8 +3465,6 @@ static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh,
struct omap_hwmod_class_sysconfig *sysc; struct omap_hwmod_class_sysconfig *sysc;
struct omap_hwmod_class *class = NULL; struct omap_hwmod_class *class = NULL;
struct omap_hwmod_ocp_if *oi = NULL; struct omap_hwmod_ocp_if *oi = NULL;
struct clockdomain *clkdm = NULL;
struct clk *clk = NULL;
void __iomem *regs = NULL; void __iomem *regs = NULL;
unsigned long flags; unsigned long flags;
@ -3511,36 +3511,6 @@ static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh,
oi->user = OCP_USER_MPU | OCP_USER_SDMA; oi->user = OCP_USER_MPU | OCP_USER_SDMA;
} }
if (!oh->_clk) {
struct clk_hw_omap *hwclk;
clk = of_clk_get_by_name(dev->of_node, "fck");
if (!IS_ERR(clk))
clk_prepare(clk);
else
clk = NULL;
/*
* Populate clockdomain based on dts clock. It is needed for
* clkdm_deny_idle() and clkdm_allow_idle() until we have have
* interconnect driver and reset driver capable of blocking
* clockdomain idle during reset, enable and idle.
*/
if (clk) {
hwclk = to_clk_hw_omap(__clk_get_hw(clk));
if (hwclk && hwclk->clkdm_name)
clkdm = clkdm_lookup(hwclk->clkdm_name);
}
/*
* Note that we assume interconnect driver manages the clocks
* and do not need to populate oh->_clk for dynamically
* allocated modules.
*/
clk_unprepare(clk);
clk_put(clk);
}
spin_lock_irqsave(&oh->_lock, flags); spin_lock_irqsave(&oh->_lock, flags);
if (regs) if (regs)
oh->_mpu_rt_va = regs; oh->_mpu_rt_va = regs;
@ -3626,7 +3596,7 @@ int omap_hwmod_init_module(struct device *dev,
u32 sysc_flags, idlemodes; u32 sysc_flags, idlemodes;
int error; int error;
if (!dev || !data) if (!dev || !data || !data->name || !cookie)
return -EINVAL; return -EINVAL;
oh = _lookup(data->name); oh = _lookup(data->name);
@ -3697,7 +3667,8 @@ int omap_hwmod_init_module(struct device *dev,
return error; return error;
return omap_hwmod_allocate_module(dev, oh, data, sysc_fields, return omap_hwmod_allocate_module(dev, oh, data, sysc_fields,
rev_offs, sysc_offs, syss_offs, cookie->clkdm, rev_offs,
sysc_offs, syss_offs,
sysc_flags, idlemodes); sysc_flags, idlemodes);
} }

View File

@ -29,6 +29,7 @@
#include <linux/platform_data/wkup_m3.h> #include <linux/platform_data/wkup_m3.h>
#include <linux/platform_data/asoc-ti-mcbsp.h> #include <linux/platform_data/asoc-ti-mcbsp.h>
#include "clockdomain.h"
#include "common.h" #include "common.h"
#include "common-board-devices.h" #include "common-board-devices.h"
#include "control.h" #include "control.h"
@ -463,6 +464,62 @@ static void __init dra7x_evm_mmc_quirk(void)
} }
#endif #endif
static struct clockdomain *ti_sysc_find_one_clockdomain(struct clk *clk)
{
struct clockdomain *clkdm = NULL;
struct clk_hw_omap *hwclk;
hwclk = to_clk_hw_omap(__clk_get_hw(clk));
if (hwclk && hwclk->clkdm_name)
clkdm = clkdm_lookup(hwclk->clkdm_name);
return clkdm;
}
/**
* ti_sysc_clkdm_init - find clockdomain based on clock
* @fck: device functional clock
* @ick: device interface clock
* @dev: struct device
*
* Populate clockdomain based on clock. It is needed for
* clkdm_deny_idle() and clkdm_allow_idle() for blocking clockdomain
* clockdomain idle during reset, enable and idle.
*
* Note that we assume interconnect driver manages the clocks
* and do not need to populate oh->_clk for dynamically
* allocated modules.
*/
static int ti_sysc_clkdm_init(struct device *dev,
struct clk *fck, struct clk *ick,
struct ti_sysc_cookie *cookie)
{
if (fck)
cookie->clkdm = ti_sysc_find_one_clockdomain(fck);
if (cookie->clkdm)
return 0;
if (ick)
cookie->clkdm = ti_sysc_find_one_clockdomain(ick);
if (cookie->clkdm)
return 0;
return -ENODEV;
}
static void ti_sysc_clkdm_deny_idle(struct device *dev,
const struct ti_sysc_cookie *cookie)
{
if (cookie->clkdm)
clkdm_deny_idle(cookie->clkdm);
}
static void ti_sysc_clkdm_allow_idle(struct device *dev,
const struct ti_sysc_cookie *cookie)
{
if (cookie->clkdm)
clkdm_allow_idle(cookie->clkdm);
}
static int ti_sysc_enable_module(struct device *dev, static int ti_sysc_enable_module(struct device *dev,
const struct ti_sysc_cookie *cookie) const struct ti_sysc_cookie *cookie)
{ {
@ -494,6 +551,9 @@ static struct of_dev_auxdata omap_auxdata_lookup[];
static struct ti_sysc_platform_data ti_sysc_pdata = { static struct ti_sysc_platform_data ti_sysc_pdata = {
.auxdata = omap_auxdata_lookup, .auxdata = omap_auxdata_lookup,
.init_clockdomain = ti_sysc_clkdm_init,
.clkdm_deny_idle = ti_sysc_clkdm_deny_idle,
.clkdm_allow_idle = ti_sysc_clkdm_allow_idle,
.init_module = omap_hwmod_init_module, .init_module = omap_hwmod_init_module,
.enable_module = ti_sysc_enable_module, .enable_module = ti_sysc_enable_module,
.idle_module = ti_sysc_idle_module, .idle_module = ti_sysc_idle_module,

View File

@ -71,6 +71,9 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = {
* @name: name if available * @name: name if available
* @revision: interconnect target module revision * @revision: interconnect target module revision
* @needs_resume: runtime resume needed on resume from suspend * @needs_resume: runtime resume needed on resume from suspend
* @clk_enable_quirk: module specific clock enable quirk
* @clk_disable_quirk: module specific clock disable quirk
* @reset_done_quirk: module specific reset done quirk
*/ */
struct sysc { struct sysc {
struct device *dev; struct device *dev;
@ -89,10 +92,14 @@ struct sysc {
struct ti_sysc_cookie cookie; struct ti_sysc_cookie cookie;
const char *name; const char *name;
u32 revision; u32 revision;
bool enabled; unsigned int enabled:1;
bool needs_resume; unsigned int needs_resume:1;
bool child_needs_resume; unsigned int child_needs_resume:1;
unsigned int disable_on_idle:1;
struct delayed_work idle_work; struct delayed_work idle_work;
void (*clk_enable_quirk)(struct sysc *sysc);
void (*clk_disable_quirk)(struct sysc *sysc);
void (*reset_done_quirk)(struct sysc *sysc);
}; };
static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
@ -100,6 +107,20 @@ static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
static void sysc_write(struct sysc *ddata, int offset, u32 value) static void sysc_write(struct sysc *ddata, int offset, u32 value)
{ {
if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) {
writew_relaxed(value & 0xffff, ddata->module_va + offset);
/* Only i2c revision has LO and HI register with stride of 4 */
if (ddata->offsets[SYSC_REVISION] >= 0 &&
offset == ddata->offsets[SYSC_REVISION]) {
u16 hi = value >> 16;
writew_relaxed(hi, ddata->module_va + offset + 4);
}
return;
}
writel_relaxed(value, ddata->module_va + offset); writel_relaxed(value, ddata->module_va + offset);
} }
@ -109,7 +130,14 @@ static u32 sysc_read(struct sysc *ddata, int offset)
u32 val; u32 val;
val = readw_relaxed(ddata->module_va + offset); val = readw_relaxed(ddata->module_va + offset);
val |= (readw_relaxed(ddata->module_va + offset + 4) << 16);
/* Only i2c revision has LO and HI register with stride of 4 */
if (ddata->offsets[SYSC_REVISION] >= 0 &&
offset == ddata->offsets[SYSC_REVISION]) {
u16 tmp = readw_relaxed(ddata->module_va + offset + 4);
val |= tmp << 16;
}
return val; return val;
} }
@ -132,6 +160,26 @@ static u32 sysc_read_revision(struct sysc *ddata)
return sysc_read(ddata, offset); return sysc_read(ddata, offset);
} }
static u32 sysc_read_sysconfig(struct sysc *ddata)
{
int offset = ddata->offsets[SYSC_SYSCONFIG];
if (offset < 0)
return 0;
return sysc_read(ddata, offset);
}
static u32 sysc_read_sysstatus(struct sysc *ddata)
{
int offset = ddata->offsets[SYSC_SYSSTATUS];
if (offset < 0)
return 0;
return sysc_read(ddata, offset);
}
static int sysc_add_named_clock_from_child(struct sysc *ddata, static int sysc_add_named_clock_from_child(struct sysc *ddata,
const char *name, const char *name,
const char *optfck_name) const char *optfck_name)
@ -422,6 +470,30 @@ static void sysc_disable_opt_clocks(struct sysc *ddata)
} }
} }
static void sysc_clkdm_deny_idle(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata;
if (ddata->legacy_mode)
return;
pdata = dev_get_platdata(ddata->dev);
if (pdata && pdata->clkdm_deny_idle)
pdata->clkdm_deny_idle(ddata->dev, &ddata->cookie);
}
static void sysc_clkdm_allow_idle(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata;
if (ddata->legacy_mode)
return;
pdata = dev_get_platdata(ddata->dev);
if (pdata && pdata->clkdm_allow_idle)
pdata->clkdm_allow_idle(ddata->dev, &ddata->cookie);
}
/** /**
* sysc_init_resets - init rstctrl reset line if configured * sysc_init_resets - init rstctrl reset line if configured
* @ddata: device driver data * @ddata: device driver data
@ -431,7 +503,7 @@ static void sysc_disable_opt_clocks(struct sysc *ddata)
static int sysc_init_resets(struct sysc *ddata) static int sysc_init_resets(struct sysc *ddata)
{ {
ddata->rsts = ddata->rsts =
devm_reset_control_array_get_optional_exclusive(ddata->dev); devm_reset_control_get_optional(ddata->dev, "rstctrl");
if (IS_ERR(ddata->rsts)) if (IS_ERR(ddata->rsts))
return PTR_ERR(ddata->rsts); return PTR_ERR(ddata->rsts);
@ -660,12 +732,6 @@ static int sysc_check_registers(struct sysc *ddata)
nr_regs++; nr_regs++;
} }
if (nr_regs < 1) {
dev_err(ddata->dev, "missing registers\n");
return -EINVAL;
}
if (nr_matches > nr_regs) { if (nr_matches > nr_regs) {
dev_err(ddata->dev, "overlapping registers: (%i/%i)", dev_err(ddata->dev, "overlapping registers: (%i/%i)",
nr_regs, nr_matches); nr_regs, nr_matches);
@ -691,12 +757,21 @@ static int sysc_ioremap(struct sysc *ddata)
{ {
int size; int size;
size = max3(ddata->offsets[SYSC_REVISION], if (ddata->offsets[SYSC_REVISION] < 0 &&
ddata->offsets[SYSC_SYSCONFIG], ddata->offsets[SYSC_SYSCONFIG] < 0 &&
ddata->offsets[SYSC_SYSSTATUS]); ddata->offsets[SYSC_SYSSTATUS] < 0) {
size = ddata->module_size;
} else {
size = max3(ddata->offsets[SYSC_REVISION],
ddata->offsets[SYSC_SYSCONFIG],
ddata->offsets[SYSC_SYSSTATUS]);
if (size < 0 || (size + sizeof(u32)) > ddata->module_size) if (size < SZ_1K)
return -EINVAL; size = SZ_1K;
if ((size + sizeof(u32)) > ddata->module_size)
size = ddata->module_size;
}
ddata->module_va = devm_ioremap(ddata->dev, ddata->module_va = devm_ioremap(ddata->dev,
ddata->module_pa, ddata->module_pa,
@ -794,7 +869,9 @@ static void sysc_show_registers(struct sysc *ddata)
} }
#define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1) #define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1)
#define SYSC_CLOCACT_ICK 2
/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */
static int sysc_enable_module(struct device *dev) static int sysc_enable_module(struct device *dev)
{ {
struct sysc *ddata; struct sysc *ddata;
@ -805,23 +882,34 @@ static int sysc_enable_module(struct device *dev)
if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV) if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV)
return 0; return 0;
/*
* TODO: Need to prevent clockdomain autoidle?
* See clkdm_deny_idle() in arch/mach-omap2/omap_hwmod.c
*/
regbits = ddata->cap->regbits; regbits = ddata->cap->regbits;
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
/* Set CLOCKACTIVITY, we only use it for ick */
if (regbits->clkact_shift >= 0 &&
(ddata->cfg.quirks & SYSC_QUIRK_USE_CLOCKACT ||
ddata->cfg.sysc_val & BIT(regbits->clkact_shift)))
reg |= SYSC_CLOCACT_ICK << regbits->clkact_shift;
/* Set SIDLE mode */ /* Set SIDLE mode */
idlemodes = ddata->cfg.sidlemodes; idlemodes = ddata->cfg.sidlemodes;
if (!idlemodes || regbits->sidle_shift < 0) if (!idlemodes || regbits->sidle_shift < 0)
goto set_midle; goto set_midle;
best_mode = fls(ddata->cfg.sidlemodes) - 1; if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_SIDLE |
if (best_mode > SYSC_IDLE_MASK) { SYSC_QUIRK_SWSUP_SIDLE_ACT)) {
dev_err(dev, "%s: invalid sidlemode\n", __func__); best_mode = SYSC_IDLE_NO;
return -EINVAL; } else {
best_mode = fls(ddata->cfg.sidlemodes) - 1;
if (best_mode > SYSC_IDLE_MASK) {
dev_err(dev, "%s: invalid sidlemode\n", __func__);
return -EINVAL;
}
/* Set WAKEUP */
if (regbits->enwkup_shift >= 0 &&
ddata->cfg.sysc_val & BIT(regbits->enwkup_shift))
reg |= BIT(regbits->enwkup_shift);
} }
reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift);
@ -832,7 +920,7 @@ set_midle:
/* Set MIDLE mode */ /* Set MIDLE mode */
idlemodes = ddata->cfg.midlemodes; idlemodes = ddata->cfg.midlemodes;
if (!idlemodes || regbits->midle_shift < 0) if (!idlemodes || regbits->midle_shift < 0)
return 0; goto set_autoidle;
best_mode = fls(ddata->cfg.midlemodes) - 1; best_mode = fls(ddata->cfg.midlemodes) - 1;
if (best_mode > SYSC_IDLE_MASK) { if (best_mode > SYSC_IDLE_MASK) {
@ -844,6 +932,14 @@ set_midle:
reg |= best_mode << regbits->midle_shift; reg |= best_mode << regbits->midle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
set_autoidle:
/* Autoidle bit must enabled separately if available */
if (regbits->autoidle_shift >= 0 &&
ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) {
reg |= 1 << regbits->autoidle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
}
return 0; return 0;
} }
@ -861,6 +957,7 @@ static int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode)
return 0; return 0;
} }
/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */
static int sysc_disable_module(struct device *dev) static int sysc_disable_module(struct device *dev)
{ {
struct sysc *ddata; struct sysc *ddata;
@ -872,11 +969,6 @@ static int sysc_disable_module(struct device *dev)
if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV) if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV)
return 0; return 0;
/*
* TODO: Need to prevent clockdomain autoidle?
* See clkdm_deny_idle() in arch/mach-omap2/omap_hwmod.c
*/
regbits = ddata->cap->regbits; regbits = ddata->cap->regbits;
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
@ -901,14 +993,21 @@ set_sidle:
if (!idlemodes || regbits->sidle_shift < 0) if (!idlemodes || regbits->sidle_shift < 0)
return 0; return 0;
ret = sysc_best_idle_mode(idlemodes, &best_mode); if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE) {
if (ret) { best_mode = SYSC_IDLE_FORCE;
dev_err(dev, "%s: invalid sidlemode\n", __func__); } else {
return ret; ret = sysc_best_idle_mode(idlemodes, &best_mode);
if (ret) {
dev_err(dev, "%s: invalid sidlemode\n", __func__);
return ret;
}
} }
reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift);
reg |= best_mode << regbits->sidle_shift; reg |= best_mode << regbits->sidle_shift;
if (regbits->autoidle_shift >= 0 &&
ddata->cfg.sysc_val & BIT(regbits->autoidle_shift))
reg |= 1 << regbits->autoidle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
return 0; return 0;
@ -932,6 +1031,9 @@ static int __maybe_unused sysc_runtime_suspend_legacy(struct device *dev,
dev_err(dev, "%s: could not idle: %i\n", dev_err(dev, "%s: could not idle: %i\n",
__func__, error); __func__, error);
if (ddata->disable_on_idle)
reset_control_assert(ddata->rsts);
return 0; return 0;
} }
@ -941,6 +1043,9 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev,
struct ti_sysc_platform_data *pdata; struct ti_sysc_platform_data *pdata;
int error; int error;
if (ddata->disable_on_idle)
reset_control_deassert(ddata->rsts);
pdata = dev_get_platdata(ddata->dev); pdata = dev_get_platdata(ddata->dev);
if (!pdata) if (!pdata)
return 0; return 0;
@ -966,14 +1071,16 @@ static int __maybe_unused sysc_runtime_suspend(struct device *dev)
if (!ddata->enabled) if (!ddata->enabled)
return 0; return 0;
sysc_clkdm_deny_idle(ddata);
if (ddata->legacy_mode) { if (ddata->legacy_mode) {
error = sysc_runtime_suspend_legacy(dev, ddata); error = sysc_runtime_suspend_legacy(dev, ddata);
if (error) if (error)
return error; goto err_allow_idle;
} else { } else {
error = sysc_disable_module(dev); error = sysc_disable_module(dev);
if (error) if (error)
return error; goto err_allow_idle;
} }
sysc_disable_main_clocks(ddata); sysc_disable_main_clocks(ddata);
@ -983,6 +1090,12 @@ static int __maybe_unused sysc_runtime_suspend(struct device *dev)
ddata->enabled = false; ddata->enabled = false;
err_allow_idle:
sysc_clkdm_allow_idle(ddata);
if (ddata->disable_on_idle)
reset_control_assert(ddata->rsts);
return error; return error;
} }
@ -996,10 +1109,15 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
if (ddata->enabled) if (ddata->enabled)
return 0; return 0;
if (ddata->disable_on_idle)
reset_control_deassert(ddata->rsts);
sysc_clkdm_deny_idle(ddata);
if (sysc_opt_clks_needed(ddata)) { if (sysc_opt_clks_needed(ddata)) {
error = sysc_enable_opt_clocks(ddata); error = sysc_enable_opt_clocks(ddata);
if (error) if (error)
return error; goto err_allow_idle;
} }
error = sysc_enable_main_clocks(ddata); error = sysc_enable_main_clocks(ddata);
@ -1018,6 +1136,8 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
ddata->enabled = true; ddata->enabled = true;
sysc_clkdm_allow_idle(ddata);
return 0; return 0;
err_main_clocks: err_main_clocks:
@ -1025,6 +1145,8 @@ err_main_clocks:
err_opt_clocks: err_opt_clocks:
if (sysc_opt_clks_needed(ddata)) if (sysc_opt_clks_needed(ddata))
sysc_disable_opt_clocks(ddata); sysc_disable_opt_clocks(ddata);
err_allow_idle:
sysc_clkdm_allow_idle(ddata);
return error; return error;
} }
@ -1106,8 +1228,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
0), 0),
SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x4fff1301, 0xffff00ff, SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x4fff1301, 0xffff00ff,
0), 0),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
/* Uarts on omap4 and later */ /* Uarts on omap4 and later */
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff, SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE),
@ -1119,6 +1243,22 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT | SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT |
SYSC_QUIRK_SWSUP_SIDLE), SYSC_QUIRK_SWSUP_SIDLE),
/* Quirks that need to be set based on detected module */
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff,
SYSC_MODULE_QUIRK_HDQ1W),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff,
SYSC_MODULE_QUIRK_HDQ1W),
SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x00000036, 0x000000ff,
SYSC_MODULE_QUIRK_I2C),
SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x0000003c, 0x000000ff,
SYSC_MODULE_QUIRK_I2C),
SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x00000040, 0x000000ff,
SYSC_MODULE_QUIRK_I2C),
SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0,
SYSC_MODULE_QUIRK_I2C),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0,
SYSC_MODULE_QUIRK_WDT),
#ifdef DEBUG #ifdef DEBUG
SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0), SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0),
SYSC_QUIRK("atl", 0, 0, -1, -1, 0x0a070100, 0xffffffff, 0), SYSC_QUIRK("atl", 0, 0, -1, -1, 0x0a070100, 0xffffffff, 0),
@ -1128,16 +1268,12 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902, SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902,
0xffff00f0, 0), 0xffff00f0, 0),
SYSC_QUIRK("dcan", 0, 0, -1, -1, 0xffffffff, 0xffffffff, 0), SYSC_QUIRK("dcan", 0, 0, -1, -1, 0xffffffff, 0xffffffff, 0),
SYSC_QUIRK("dcan", 0, 0, -1, -1, 0x00001401, 0xffffffff, 0),
SYSC_QUIRK("dmic", 0, 0, 0x10, -1, 0x50010000, 0xffffffff, 0), SYSC_QUIRK("dmic", 0, 0, 0x10, -1, 0x50010000, 0xffffffff, 0),
SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0), SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0),
SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0), SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0),
SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0), SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, 0),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, 0),
SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0), SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0),
SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0), SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0),
SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0, 0),
SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, 0), SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, 0),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0), SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0),
SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44307b02, 0xffffffff, 0), SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44307b02, 0xffffffff, 0),
@ -1173,7 +1309,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0),
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, 0), 0xffffffff, 0),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 0),
SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0), SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0),
#endif #endif
}; };
@ -1246,6 +1381,121 @@ static void sysc_init_revision_quirks(struct sysc *ddata)
} }
} }
/* 1-wire needs module's internal clocks enabled for reset */
static void sysc_clk_enable_quirk_hdq1w(struct sysc *ddata)
{
int offset = 0x0c; /* HDQ_CTRL_STATUS */
u16 val;
val = sysc_read(ddata, offset);
val |= BIT(5);
sysc_write(ddata, offset, val);
}
/* I2C needs extra enable bit toggling for reset */
static void sysc_clk_quirk_i2c(struct sysc *ddata, bool enable)
{
int offset;
u16 val;
/* I2C_CON, omap2/3 is different from omap4 and later */
if ((ddata->revision & 0xffffff00) == 0x001f0000)
offset = 0x24;
else
offset = 0xa4;
/* I2C_EN */
val = sysc_read(ddata, offset);
if (enable)
val |= BIT(15);
else
val &= ~BIT(15);
sysc_write(ddata, offset, val);
}
static void sysc_clk_enable_quirk_i2c(struct sysc *ddata)
{
sysc_clk_quirk_i2c(ddata, true);
}
static void sysc_clk_disable_quirk_i2c(struct sysc *ddata)
{
sysc_clk_quirk_i2c(ddata, false);
}
/* Watchdog timer needs a disable sequence after reset */
static void sysc_reset_done_quirk_wdt(struct sysc *ddata)
{
int wps, spr, error;
u32 val;
wps = 0x34;
spr = 0x48;
sysc_write(ddata, spr, 0xaaaa);
error = readl_poll_timeout(ddata->module_va + wps, val,
!(val & 0x10), 100,
MAX_MODULE_SOFTRESET_WAIT);
if (error)
dev_warn(ddata->dev, "wdt disable spr failed\n");
sysc_write(ddata, wps, 0x5555);
error = readl_poll_timeout(ddata->module_va + wps, val,
!(val & 0x10), 100,
MAX_MODULE_SOFTRESET_WAIT);
if (error)
dev_warn(ddata->dev, "wdt disable wps failed\n");
}
static void sysc_init_module_quirks(struct sysc *ddata)
{
if (ddata->legacy_mode || !ddata->name)
return;
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_HDQ1W) {
ddata->clk_enable_quirk = sysc_clk_enable_quirk_hdq1w;
return;
}
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_I2C) {
ddata->clk_enable_quirk = sysc_clk_enable_quirk_i2c;
ddata->clk_disable_quirk = sysc_clk_disable_quirk_i2c;
return;
}
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_WDT)
ddata->reset_done_quirk = sysc_reset_done_quirk_wdt;
}
static int sysc_clockdomain_init(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
struct clk *fck = NULL, *ick = NULL;
int error;
if (!pdata || !pdata->init_clockdomain)
return 0;
switch (ddata->nr_clocks) {
case 2:
ick = ddata->clocks[SYSC_ICK];
/* fallthrough */
case 1:
fck = ddata->clocks[SYSC_FCK];
break;
case 0:
return 0;
}
error = pdata->init_clockdomain(ddata->dev, fck, ick, &ddata->cookie);
if (!error || error == -ENODEV)
return 0;
return error;
}
/* /*
* Note that pdata->init_module() typically does a reset first. After * Note that pdata->init_module() typically does a reset first. After
* pdata->init_module() is done, PM runtime can be used for the interconnect * pdata->init_module() is done, PM runtime can be used for the interconnect
@ -1256,7 +1506,7 @@ static int sysc_legacy_init(struct sysc *ddata)
struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
int error; int error;
if (!ddata->legacy_mode || !pdata || !pdata->init_module) if (!pdata || !pdata->init_module)
return 0; return 0;
error = pdata->init_module(ddata->dev, ddata->mdata, &ddata->cookie); error = pdata->init_module(ddata->dev, ddata->mdata, &ddata->cookie);
@ -1281,7 +1531,7 @@ static int sysc_legacy_init(struct sysc *ddata)
*/ */
static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset) static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset)
{ {
int error; int error, val;
if (!ddata->rsts) if (!ddata->rsts)
return 0; return 0;
@ -1292,37 +1542,68 @@ static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset)
return error; return error;
} }
return reset_control_deassert(ddata->rsts); error = reset_control_deassert(ddata->rsts);
if (error == -EEXIST)
return 0;
error = readx_poll_timeout(reset_control_status, ddata->rsts, val,
val == 0, 100, MAX_MODULE_SOFTRESET_WAIT);
return error;
} }
/*
* Note that the caller must ensure the interconnect target module is enabled
* before calling reset. Otherwise reset will not complete.
*/
static int sysc_reset(struct sysc *ddata) static int sysc_reset(struct sysc *ddata)
{ {
int offset = ddata->offsets[SYSC_SYSCONFIG]; int sysc_offset, syss_offset, sysc_val, rstval, quirks, error = 0;
int val; u32 sysc_mask, syss_done;
if (ddata->legacy_mode || offset < 0 || sysc_offset = ddata->offsets[SYSC_SYSCONFIG];
syss_offset = ddata->offsets[SYSC_SYSSTATUS];
quirks = ddata->cfg.quirks;
if (ddata->legacy_mode || sysc_offset < 0 ||
ddata->cap->regbits->srst_shift < 0 ||
ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)
return 0; return 0;
/* sysc_mask = BIT(ddata->cap->regbits->srst_shift);
* Currently only support reset status in sysstatus.
* Warn and return error in all other cases
*/
if (!ddata->cfg.syss_mask) {
dev_err(ddata->dev, "No ti,syss-mask. Reset failed\n");
return -EINVAL;
}
val = sysc_read(ddata, offset); if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED)
val |= (0x1 << ddata->cap->regbits->srst_shift); syss_done = 0;
sysc_write(ddata, offset, val); else
syss_done = ddata->cfg.syss_mask;
if (ddata->clk_disable_quirk)
ddata->clk_disable_quirk(ddata);
sysc_val = sysc_read_sysconfig(ddata);
sysc_val |= sysc_mask;
sysc_write(ddata, sysc_offset, sysc_val);
if (ddata->clk_enable_quirk)
ddata->clk_enable_quirk(ddata);
/* Poll on reset status */ /* Poll on reset status */
offset = ddata->offsets[SYSC_SYSSTATUS]; if (syss_offset >= 0) {
error = readx_poll_timeout(sysc_read_sysstatus, ddata, rstval,
(rstval & ddata->cfg.syss_mask) ==
syss_done,
100, MAX_MODULE_SOFTRESET_WAIT);
return readl_poll_timeout(ddata->module_va + offset, val, } else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) {
(val & ddata->cfg.syss_mask) == 0x0, error = readx_poll_timeout(sysc_read_sysconfig, ddata, rstval,
100, MAX_MODULE_SOFTRESET_WAIT); !(rstval & sysc_mask),
100, MAX_MODULE_SOFTRESET_WAIT);
}
if (ddata->reset_done_quirk)
ddata->reset_done_quirk(ddata);
return error;
} }
/* /*
@ -1335,12 +1616,8 @@ static int sysc_init_module(struct sysc *ddata)
{ {
int error = 0; int error = 0;
bool manage_clocks = true; bool manage_clocks = true;
bool reset = true;
if (ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) error = sysc_rstctrl_reset_deassert(ddata, false);
reset = false;
error = sysc_rstctrl_reset_deassert(ddata, reset);
if (error) if (error)
return error; return error;
@ -1348,7 +1625,13 @@ static int sysc_init_module(struct sysc *ddata)
(SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT)) (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))
manage_clocks = false; manage_clocks = false;
error = sysc_clockdomain_init(ddata);
if (error)
return error;
if (manage_clocks) { if (manage_clocks) {
sysc_clkdm_deny_idle(ddata);
error = sysc_enable_opt_clocks(ddata); error = sysc_enable_opt_clocks(ddata);
if (error) if (error)
return error; return error;
@ -1358,23 +1641,43 @@ static int sysc_init_module(struct sysc *ddata)
goto err_opt_clocks; goto err_opt_clocks;
} }
if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) {
error = sysc_rstctrl_reset_deassert(ddata, true);
if (error)
goto err_main_clocks;
}
ddata->revision = sysc_read_revision(ddata); ddata->revision = sysc_read_revision(ddata);
sysc_init_revision_quirks(ddata); sysc_init_revision_quirks(ddata);
sysc_init_module_quirks(ddata);
error = sysc_legacy_init(ddata); if (ddata->legacy_mode) {
if (error) error = sysc_legacy_init(ddata);
goto err_main_clocks; if (error)
goto err_main_clocks;
}
if (!ddata->legacy_mode && manage_clocks) {
error = sysc_enable_module(ddata->dev);
if (error)
goto err_main_clocks;
}
error = sysc_reset(ddata); error = sysc_reset(ddata);
if (error) if (error)
dev_err(ddata->dev, "Reset failed with %d\n", error); dev_err(ddata->dev, "Reset failed with %d\n", error);
if (!ddata->legacy_mode && manage_clocks)
sysc_disable_module(ddata->dev);
err_main_clocks: err_main_clocks:
if (manage_clocks) if (manage_clocks)
sysc_disable_main_clocks(ddata); sysc_disable_main_clocks(ddata);
err_opt_clocks: err_opt_clocks:
if (manage_clocks) if (manage_clocks) {
sysc_disable_opt_clocks(ddata); sysc_disable_opt_clocks(ddata);
sysc_clkdm_allow_idle(ddata);
}
return error; return error;
} }
@ -1664,9 +1967,6 @@ static struct dev_pm_domain sysc_child_pm_domain = {
*/ */
static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child) static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child)
{ {
if (!ddata->legacy_mode)
return;
if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
dev_pm_domain_set(child, &sysc_child_pm_domain); dev_pm_domain_set(child, &sysc_child_pm_domain);
} }
@ -2006,6 +2306,7 @@ static const struct sysc_capabilities sysc_dra7_mcan = {
.type = TI_SYSC_DRA7_MCAN, .type = TI_SYSC_DRA7_MCAN,
.sysc_mask = SYSC_DRA7_MCAN_ENAWAKEUP | SYSC_OMAP4_SOFTRESET, .sysc_mask = SYSC_DRA7_MCAN_ENAWAKEUP | SYSC_OMAP4_SOFTRESET,
.regbits = &sysc_regbits_dra7_mcan, .regbits = &sysc_regbits_dra7_mcan,
.mod_quirks = SYSS_QUIRK_RESETDONE_INVERTED,
}; };
static int sysc_init_pdata(struct sysc *ddata) static int sysc_init_pdata(struct sysc *ddata)
@ -2013,20 +2314,22 @@ static int sysc_init_pdata(struct sysc *ddata)
struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
struct ti_sysc_module_data *mdata; struct ti_sysc_module_data *mdata;
if (!pdata || !ddata->legacy_mode) if (!pdata)
return 0; return 0;
mdata = devm_kzalloc(ddata->dev, sizeof(*mdata), GFP_KERNEL); mdata = devm_kzalloc(ddata->dev, sizeof(*mdata), GFP_KERNEL);
if (!mdata) if (!mdata)
return -ENOMEM; return -ENOMEM;
mdata->name = ddata->legacy_mode; if (ddata->legacy_mode) {
mdata->module_pa = ddata->module_pa; mdata->name = ddata->legacy_mode;
mdata->module_size = ddata->module_size; mdata->module_pa = ddata->module_pa;
mdata->offsets = ddata->offsets; mdata->module_size = ddata->module_size;
mdata->nr_offsets = SYSC_MAX_REGS; mdata->offsets = ddata->offsets;
mdata->cap = ddata->cap; mdata->nr_offsets = SYSC_MAX_REGS;
mdata->cfg = &ddata->cfg; mdata->cap = ddata->cap;
mdata->cfg = &ddata->cfg;
}
ddata->mdata = mdata; ddata->mdata = mdata;
@ -2146,7 +2449,7 @@ static int sysc_probe(struct platform_device *pdev)
} }
if (!of_get_available_child_count(ddata->dev->of_node)) if (!of_get_available_child_count(ddata->dev->of_node))
reset_control_assert(ddata->rsts); ddata->disable_on_idle = true;
return 0; return 0;

View File

@ -19,6 +19,7 @@ enum ti_sysc_module_type {
struct ti_sysc_cookie { struct ti_sysc_cookie {
void *data; void *data;
void *clkdm;
}; };
/** /**
@ -46,6 +47,10 @@ struct sysc_regbits {
s8 emufree_shift; s8 emufree_shift;
}; };
#define SYSC_MODULE_QUIRK_HDQ1W BIT(17)
#define SYSC_MODULE_QUIRK_I2C BIT(16)
#define SYSC_MODULE_QUIRK_WDT BIT(15)
#define SYSS_QUIRK_RESETDONE_INVERTED BIT(14)
#define SYSC_QUIRK_SWSUP_MSTANDBY BIT(13) #define SYSC_QUIRK_SWSUP_MSTANDBY BIT(13)
#define SYSC_QUIRK_SWSUP_SIDLE_ACT BIT(12) #define SYSC_QUIRK_SWSUP_SIDLE_ACT BIT(12)
#define SYSC_QUIRK_SWSUP_SIDLE BIT(11) #define SYSC_QUIRK_SWSUP_SIDLE BIT(11)
@ -125,9 +130,16 @@ struct ti_sysc_module_data {
}; };
struct device; struct device;
struct clk;
struct ti_sysc_platform_data { struct ti_sysc_platform_data {
struct of_dev_auxdata *auxdata; struct of_dev_auxdata *auxdata;
int (*init_clockdomain)(struct device *dev, struct clk *fck,
struct clk *ick, struct ti_sysc_cookie *cookie);
void (*clkdm_deny_idle)(struct device *dev,
const struct ti_sysc_cookie *cookie);
void (*clkdm_allow_idle)(struct device *dev,
const struct ti_sysc_cookie *cookie);
int (*init_module)(struct device *dev, int (*init_module)(struct device *dev,
const struct ti_sysc_module_data *data, const struct ti_sysc_module_data *data,
struct ti_sysc_cookie *cookie); struct ti_sysc_cookie *cookie);