* Re: [PATCH v3 4/4] iio: magnetometer: ak8975: Add gpio reset support
From: Jonathan Albrieux @ 2020-05-19 14:10 UTC (permalink / raw)
To: Stephan Gerhold
Cc: linux-kernel, ~postmarketos/upstreaming, Andy Shevchenko,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Greg Kroah-Hartman, Hartmut Knaack, Jilayne Lovejoy,
Jonathan Cameron, Kate Stewart, Lars-Peter Clausen, Linus Walleij,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Steve Winslow, Thomas Gleixner, Jonathan Cameron, Allison Randal
In-Reply-To: <20200519132512.GC4623@gerhold.net>
On Tue, May 19, 2020 at 03:25:12PM +0200, Stephan Gerhold wrote:
> On Tue, May 19, 2020 at 02:43:54PM +0200, Jonathan Albrieux wrote:
> > According to AK09911 datasheet, if reset gpio is provided then
> > deassert reset on ak8975_power_on() and assert reset on ak8975_power_off().
> >
> > Without reset's deassertion during ak8975_power_on(), driver's probe fails
> > on ak8975_who_i_am while() checking for device identity for AK09911 chip.
> >
> > AK09911 has an active low reset gpio to handle register's reset.
> > AK09911 datasheed says that, if not used, reset pin should be connected
>
> Another minor typo: datasheed -> datasheet
>
> In any case, FWIW:
> Reviewed-by: Stephan Gerhold <stephan@gerhold.net>
>
> > to VID. This patch emulates this situation.
> >
> > Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> > ---
> > drivers/iio/magnetometer/ak8975.c | 18 ++++++++++++++++++
> > 1 file changed, 18 insertions(+)
> >
> > diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
> > index fd368455cd7b..a23422aad97d 100644
> > --- a/drivers/iio/magnetometer/ak8975.c
> > +++ b/drivers/iio/magnetometer/ak8975.c
> > @@ -358,6 +358,7 @@ struct ak8975_data {
> > u8 asa[3];
> > long raw_to_gauss[3];
> > struct gpio_desc *eoc_gpiod;
> > + struct gpio_desc *reset_gpiod;
> > int eoc_irq;
> > wait_queue_head_t data_ready_queue;
> > unsigned long flags;
> > @@ -384,6 +385,9 @@ static int ak8975_power_on(const struct ak8975_data *data)
> > "Failed to enable specified Vid supply\n");
> > return ret;
> > }
> > +
> > + gpiod_set_value_cansleep(data->reset_gpiod, 0);
> > +
> > /*
> > * According to the datasheet the power supply rise time is 200us
> > * and the minimum wait time before mode setting is 100us, in
> > @@ -396,6 +400,8 @@ static int ak8975_power_on(const struct ak8975_data *data)
> > /* Disable attached power regulator if any. */
> > static void ak8975_power_off(const struct ak8975_data *data)
> > {
> > + gpiod_set_value_cansleep(data->reset_gpiod, 1);
> > +
> > regulator_disable(data->vid);
> > regulator_disable(data->vdd);
> > }
> > @@ -839,6 +845,7 @@ static int ak8975_probe(struct i2c_client *client,
> > struct ak8975_data *data;
> > struct iio_dev *indio_dev;
> > struct gpio_desc *eoc_gpiod;
> > + struct gpio_desc *reset_gpiod;
> > const void *match;
> > unsigned int i;
> > int err;
> > @@ -856,6 +863,16 @@ static int ak8975_probe(struct i2c_client *client,
> > if (eoc_gpiod)
> > gpiod_set_consumer_name(eoc_gpiod, "ak_8975");
> >
> > + /*
> > + * According to AK09911 datasheet, if reset GPIO is provided then
> > + * deassert reset on ak8975_power_on() and assert reset on
> > + * ak8975_power_off().
> > + */
> > + reset_gpiod = devm_gpiod_get_optional(&client->dev,
> > + "reset", GPIOD_OUT_HIGH);
> > + if (IS_ERR(reset_gpiod))
> > + return PTR_ERR(reset_gpiod);
> > +
> > /* Register with IIO */
> > indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> > if (indio_dev == NULL)
> > @@ -866,6 +883,7 @@ static int ak8975_probe(struct i2c_client *client,
> >
> > data->client = client;
> > data->eoc_gpiod = eoc_gpiod;
> > + data->reset_gpiod = reset_gpiod;
> > data->eoc_irq = 0;
> >
> > err = iio_read_mount_matrix(&client->dev, "mount-matrix", &data->orientation);
> > --
> > 2.17.1
Thank you, will fix that typo too,
Best regards,
Jonathan Albrieux
^ permalink raw reply
* Re: [PATCH 3/3] hwmon: (ina2xx) Add support for ina260
From: Guenter Roeck @ 2020-05-19 14:14 UTC (permalink / raw)
To: Michal Simek, Franz Forstmayr
Cc: Jean Delvare, Rob Herring, Mark Rutland, Jonathan Corbet,
linux-hwmon, devicetree, linux-kernel, linux-doc
In-Reply-To: <bfa786b6-fe62-a5fb-718f-bb9e95b1f051@xilinx.com>
On 5/18/20 10:21 PM, Michal Simek wrote:
> On 26. 02. 20 3:16, Guenter Roeck wrote:
>> On 2/24/20 3:26 PM, Franz Forstmayr wrote:
>>> Add initial support for INA260 power monitor with integrated shunt.
>>> Registers are different from other INA2xx devices, that's why a small
>>> translation table is used.
>>>
>>> Signed-off-by: Franz Forstmayr <forstmayr.franz@gmail.com>
>>
>> I think the chip is sufficiently different to other chips that a separate
>> driver would make much more sense than adding support to the existing
>> driver.
>> There is no calibration, registers are different, the retry logic is
>> not needed. A new driver could use the with_info API and would be much
>> simpler while at the same time not messing up the existing driver.
>
> Isn't it also better to switch to IIO framework?
> As we discussed in past there are two ina226 drivers. One in hwmon and
> second based on IIO framework (more advance one?) and would be good to
> deprecate hwmon one.
"More advanced" is relative. The ina2xx driver in iio doesn't support
alert limits (which is queued in the hwmon driver for 5.8), and the
iio->hwmon bridge doesn't support it either. On top of that, there are
existing users of the hwmon driver, which would have to be converted
first. As for ina260, it would be up to the implementer to determine
if alert limit support is needed or not, and which API would be
appropriate for the intended use case.
Guenter
^ permalink raw reply
* [PATCH net-next 0/4] DP83869 Enhancements
From: Dan Murphy @ 2020-05-19 14:18 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
Hello
These are improvements to the DP83869 Ethernet PHY driver. OP-mode and port
mirroring may be strapped on the device but the software only retrives these
settings from the device tree. Reading the straps and initializing the
associated stored variables so when setting the PHY up and down the PHY's
configuration values will be retained.
The PHY also supports RGMII internal delays. Implement this feature as it
was done in the DP83867 device.
Dan Murphy (4):
net: phy: dp83869: Update port-mirroring to read straps
net: phy: dp83869: Set opmode from straps
dt-bindings: net: Add RGMII internal delay for DP83869
net: dp83869: Add RGMII internal delay configuration
.../devicetree/bindings/net/ti,dp83869.yaml | 16 +++
drivers/net/phy/dp83869.c | 120 +++++++++++++++++-
include/dt-bindings/net/ti-dp83869.h | 18 +++
3 files changed, 150 insertions(+), 4 deletions(-)
--
2.26.2
^ permalink raw reply
* [PATCH net-next 2/4] net: phy: dp83869: Set opmode from straps
From: Dan Murphy @ 2020-05-19 14:18 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200519141813.28167-1-dmurphy@ti.com>
If the op-mode for the device is not set in the device tree then set
the strapped op-mode and store it for later configuration.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/dp83869.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index 073a0f7754a5..64fa2d911074 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -65,6 +65,7 @@
#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0)
/* STRAP_STS1 bits */
+#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0)
#define DP83869_STRAP_STS1_RESERVED BIT(11)
#define DP83869_STRAP_MIRROR_ENABLED BIT(12)
@@ -161,6 +162,20 @@ static int dp83869_config_port_mirroring(struct phy_device *phydev)
DP83869_CFG3_PORT_MIRROR_EN);
}
+static int dp83869_set_strapped_mode(struct phy_device *phydev)
+{
+ struct dp83869_private *dp83869 = phydev->priv;
+ u16 val;
+
+ val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
+ if (val < 0)
+ return val;
+
+ dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK;
+
+ return 0;
+}
+
#ifdef CONFIG_OF_MDIO
static int dp83869_of_init(struct phy_device *phydev)
{
@@ -185,6 +200,10 @@ static int dp83869_of_init(struct phy_device *phydev)
if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
return -EINVAL;
+ } else {
+ ret = dp83869_set_strapped_mode(phydev);
+ if (ret)
+ return ret;
}
if (of_property_read_bool(of_node, "ti,max-output-impedance"))
@@ -218,7 +237,7 @@ static int dp83869_of_init(struct phy_device *phydev)
#else
static int dp83869_of_init(struct phy_device *phydev)
{
- return 0;
+ return dp83869_set_strapped_mode(phydev);
}
#endif /* CONFIG_OF_MDIO */
--
2.26.2
^ permalink raw reply related
* [PATCH net-next 4/4] net: dp83869: Add RGMII internal delay configuration
From: Dan Murphy @ 2020-05-19 14:18 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200519141813.28167-1-dmurphy@ti.com>
Add RGMII internal delay configuration for Rx and Tx.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/dp83869.c | 84 +++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index 64fa2d911074..7d0b11220e47 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -99,6 +99,14 @@
#define DP83869_OP_MODE_MII BIT(5)
#define DP83869_SGMII_RGMII_BRIDGE BIT(6)
+/* RGMIIDCTL bits */
+#define DP83869_RGMII_TX_CLK_DELAY_MAX 0xf
+#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83869_RGMII_TX_CLK_DELAY_INV (DP83869_RGMII_TX_CLK_DELAY_MAX + 1)
+#define DP83869_RGMII_RX_CLK_DELAY_MAX 0xf
+#define DP83869_RGMII_RX_CLK_DELAY_SHIFT 0
+#define DP83869_RGMII_RX_CLK_DELAY_INV (DP83869_RGMII_RX_CLK_DELAY_MAX + 1)
+
enum {
DP83869_PORT_MIRRORING_KEEP,
DP83869_PORT_MIRRORING_EN,
@@ -108,6 +116,8 @@ enum {
struct dp83869_private {
int tx_fifo_depth;
int rx_fifo_depth;
+ u32 rx_id_delay;
+ u32 tx_id_delay;
int io_impedance;
int port_mirroring;
bool rxctrl_strap_quirk;
@@ -232,6 +242,26 @@ static int dp83869_of_init(struct phy_device *phydev)
&dp83869->tx_fifo_depth))
dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
+ dp83869->rx_id_delay = DP83869_RGMII_RX_CLK_DELAY_INV;
+ ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
+ &dp83869->rx_id_delay);
+ if (!ret && dp83869->rx_id_delay > DP83869_RGMII_RX_CLK_DELAY_MAX) {
+ phydev_err(phydev,
+ "ti,rx-internal-delay value of %u out of range\n",
+ dp83869->rx_id_delay);
+ return -EINVAL;
+ }
+
+ dp83869->tx_id_delay = DP83869_RGMII_TX_CLK_DELAY_INV;
+ ret = of_property_read_u32(of_node, "ti,tx-internal-delay",
+ &dp83869->tx_id_delay);
+ if (!ret && dp83869->tx_id_delay > DP83869_RGMII_TX_CLK_DELAY_MAX) {
+ phydev_err(phydev,
+ "ti,tx-internal-delay value of %u out of range\n",
+ dp83869->tx_id_delay);
+ return -EINVAL;
+ }
+
return ret;
}
#else
@@ -270,6 +300,29 @@ static int dp83869_configure_rgmii(struct phy_device *phydev,
return ret;
}
+static int dp83869_verify_rgmii_cfg(struct phy_device *phydev)
+{
+ struct dp83869_private *dp83869 = phydev->priv;
+
+ /* RX delay *must* be specified if internal delay of RX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) &&
+ dp83869->rx_id_delay == DP83869_RGMII_RX_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,rx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ /* TX delay *must* be specified if internal delay of TX is used. */
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) &&
+ dp83869->tx_id_delay == DP83869_RGMII_TX_CLK_DELAY_INV) {
+ phydev_err(phydev, "ti,tx-internal-delay must be specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int dp83869_configure_mode(struct phy_device *phydev,
struct dp83869_private *dp83869)
{
@@ -371,6 +424,11 @@ static int dp83869_config_init(struct phy_device *phydev)
{
struct dp83869_private *dp83869 = phydev->priv;
int ret, val;
+ u16 delay;
+
+ ret = dp83869_verify_rgmii_cfg(phydev);
+ if (ret)
+ return ret;
ret = dp83869_configure_mode(phydev, dp83869);
if (ret)
@@ -394,6 +452,32 @@ static int dp83869_config_init(struct phy_device *phydev)
dp83869->clk_output_sel <<
DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT);
+ if (phy_interface_is_rgmii(phydev)) {
+ val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL);
+
+ val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | DP83869_RGMII_RX_CLK_DELAY_EN);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ val |= (DP83869_RGMII_TX_CLK_DELAY_EN | DP83869_RGMII_RX_CLK_DELAY_EN);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ val |= DP83869_RGMII_TX_CLK_DELAY_EN;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ val |= DP83869_RGMII_RX_CLK_DELAY_EN;
+
+ phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, val);
+
+ delay = 0;
+ if (dp83869->rx_id_delay != DP83869_RGMII_RX_CLK_DELAY_INV)
+ delay |= dp83869->rx_id_delay;
+ if (dp83869->tx_id_delay != DP83869_RGMII_TX_CLK_DELAY_INV)
+ delay |= dp83869->tx_id_delay <<
+ DP83869_RGMII_TX_CLK_DELAY_SHIFT;
+
+ phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL,
+ delay);
+ }
+
return ret;
}
--
2.26.2
^ permalink raw reply related
* [PATCH net-next 3/4] dt-bindings: net: Add RGMII internal delay for DP83869
From: Dan Murphy @ 2020-05-19 14:18 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200519141813.28167-1-dmurphy@ti.com>
Add the internal delay values into the header and update the binding
with the internal delay properties.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
.../devicetree/bindings/net/ti,dp83869.yaml | 16 ++++++++++++++++
include/dt-bindings/net/ti-dp83869.h | 18 ++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/ti,dp83869.yaml b/Documentation/devicetree/bindings/net/ti,dp83869.yaml
index 5b69ef03bbf7..344015ab9081 100644
--- a/Documentation/devicetree/bindings/net/ti,dp83869.yaml
+++ b/Documentation/devicetree/bindings/net/ti,dp83869.yaml
@@ -64,6 +64,20 @@ properties:
Operational mode for the PHY. If this is not set then the operational
mode is set by the straps. see dt-bindings/net/ti-dp83869.h for values
+ ti,rx-internal-delay:
+ $ref: /schemas/types.yaml#definitions/uint32
+ description: |
+ RGMII Receive Clock Delay - see dt-bindings/net/ti-dp83869.h
+ for applicable values. Required only if interface type is
+ PHY_INTERFACE_MODE_RGMII_ID or PHY_INTERFACE_MODE_RGMII_RXID.
+
+ ti,tx-internal-delay:
+ $ref: /schemas/types.yaml#definitions/uint32
+ description: |
+ RGMII Transmit Clock Delay - see dt-bindings/net/ti-dp83869.h
+ for applicable values. Required only if interface type is
+ PHY_INTERFACE_MODE_RGMII_ID or PHY_INTERFACE_MODE_RGMII_TXID.
+
required:
- reg
@@ -80,5 +94,7 @@ examples:
ti,op-mode = <DP83869_RGMII_COPPER_ETHERNET>;
ti,max-output-impedance = "true";
ti,clk-output-sel = <DP83869_CLK_O_SEL_CHN_A_RCLK>;
+ ti,rx-internal-delay = <DP83869_RGMIIDCTL_2_25_NS>;
+ ti,tx-internal-delay = <DP83869_RGMIIDCTL_2_75_NS>;
};
};
diff --git a/include/dt-bindings/net/ti-dp83869.h b/include/dt-bindings/net/ti-dp83869.h
index 218b1a64e975..77d104a40f1f 100644
--- a/include/dt-bindings/net/ti-dp83869.h
+++ b/include/dt-bindings/net/ti-dp83869.h
@@ -16,6 +16,24 @@
#define DP83869_PHYCR_FIFO_DEPTH_6_B_NIB 0x02
#define DP83869_PHYCR_FIFO_DEPTH_8_B_NIB 0x03
+/* RGMIIDCTL internal delay for rx and tx */
+#define DP83869_RGMIIDCTL_250_PS 0x0
+#define DP83869_RGMIIDCTL_500_PS 0x1
+#define DP83869_RGMIIDCTL_750_PS 0x2
+#define DP83869_RGMIIDCTL_1_NS 0x3
+#define DP83869_RGMIIDCTL_1_25_NS 0x4
+#define DP83869_RGMIIDCTL_1_50_NS 0x5
+#define DP83869_RGMIIDCTL_1_75_NS 0x6
+#define DP83869_RGMIIDCTL_2_00_NS 0x7
+#define DP83869_RGMIIDCTL_2_25_NS 0x8
+#define DP83869_RGMIIDCTL_2_50_NS 0x9
+#define DP83869_RGMIIDCTL_2_75_NS 0xa
+#define DP83869_RGMIIDCTL_3_00_NS 0xb
+#define DP83869_RGMIIDCTL_3_25_NS 0xc
+#define DP83869_RGMIIDCTL_3_50_NS 0xd
+#define DP83869_RGMIIDCTL_3_75_NS 0xe
+#define DP83869_RGMIIDCTL_4_00_NS 0xf
+
/* IO_MUX_CFG - Clock output selection */
#define DP83869_CLK_O_SEL_CHN_A_RCLK 0x0
#define DP83869_CLK_O_SEL_CHN_B_RCLK 0x1
--
2.26.2
^ permalink raw reply related
* [PATCH net-next 1/4] net: phy: dp83869: Update port-mirroring to read straps
From: Dan Murphy @ 2020-05-19 14:18 UTC (permalink / raw)
To: andrew, f.fainelli, hkallweit1, davem
Cc: netdev, linux-kernel, devicetree, Dan Murphy
In-Reply-To: <20200519141813.28167-1-dmurphy@ti.com>
The device tree may not have the property set for port mirroring
because the hardware may have it strapped. If the property is not in the
DT then check the straps and set the port mirroring bit appropriately.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/net/phy/dp83869.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index 7996a4aea8d2..073a0f7754a5 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -66,6 +66,7 @@
/* STRAP_STS1 bits */
#define DP83869_STRAP_STS1_RESERVED BIT(11)
+#define DP83869_STRAP_MIRROR_ENABLED BIT(12)
/* PHYCTRL bits */
#define DP83869_RX_FIFO_SHIFT 12
@@ -191,10 +192,18 @@ static int dp83869_of_init(struct phy_device *phydev)
else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN;
- if (of_property_read_bool(of_node, "enet-phy-lane-swap"))
+ if (of_property_read_bool(of_node, "enet-phy-lane-swap")) {
dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
- else
- dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
+ } else {
+ /* If the lane swap is not in the DT then check the straps */
+ ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
+ if (ret < 0)
+ return ret;
+ if (ret & DP83869_STRAP_MIRROR_ENABLED)
+ dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
+ else
+ dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
+ }
if (of_property_read_u32(of_node, "rx-fifo-depth",
&dp83869->rx_fifo_depth))
--
2.26.2
^ permalink raw reply related
* [PATCH v7 0/3] Add JEITA properties and introduce the bq2515x charger
From: Dan Murphy @ 2020-05-19 14:19 UTC (permalink / raw)
To: sre; +Cc: sspatil, linux-pm, robh, linux-kernel, devicetree, Dan Murphy
Hello,
This patchset adds additional health properties to the power_supply header.
These additional properties are taken from the JEITA specification. This
patchset also introduces the bq2515x family of charging ICs.
Dan Murphy (1):
power_supply: Add additional health properties to the header
Ricardo Rivera-Matos (2):
Add the bindings for the bq25150 and bq25155 500mA charging ICs from
Texas Instruments.
power: supply: bq25150 introduce the bq25150
Documentation/ABI/testing/sysfs-class-power | 2 +-
.../bindings/power/supply/bq2515x.yaml | 106 ++
drivers/power/supply/Kconfig | 13 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/bq2515x_charger.c | 1167 +++++++++++++++++
drivers/power/supply/power_supply_sysfs.c | 2 +-
include/linux/power_supply.h | 3 +
7 files changed, 1292 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml
create mode 100644 drivers/power/supply/bq2515x_charger.c
--
2.26.2
^ permalink raw reply
* [PATCH v7 3/3] power: supply: bq25150 introduce the bq25150
From: Dan Murphy @ 2020-05-19 14:19 UTC (permalink / raw)
To: sre; +Cc: sspatil, linux-pm, robh, linux-kernel, devicetree,
Ricardo Rivera-Matos
In-Reply-To: <20200519141916.28282-1-dmurphy@ti.com>
From: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
Introduce the bq2515x family of chargers.
The BQ2515X family of devices are highly integrated battery management
ICs that integrate the most common functions for wearable devices
namely a charger, an output voltage rail, ADC for battery and system
monitoring, and a push-button controller.
Datasheets:
bq25150 - http://www.ti.com/lit/ds/symlink/bq25150.pdf
bq25155 - http://www.ti.com/lit/ds/symlink/bq25155.pdf
Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
---
drivers/power/supply/Kconfig | 13 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/bq2515x_charger.c | 1167 ++++++++++++++++++++++++
3 files changed, 1181 insertions(+)
create mode 100644 drivers/power/supply/bq2515x_charger.c
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index f3424fdce341..266193301e2d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -589,6 +589,19 @@ config CHARGER_BQ24735
help
Say Y to enable support for the TI BQ24735 battery charger.
+config CHARGER_BQ2515X
+ tristate "TI BQ2515X battery charger family"
+ depends on I2C
+ depends on GPIOLIB || COMPILE_TEST
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the TI BQ2515X family of battery
+ charging integrated circuits. The BQ2515X are highly integrated
+ battery charge management ICs that integrate the most common
+ functions for wearable devices, namely a charger, an output voltage
+ rail, ADC for battery and system monitoring, and push-button
+ controller.
+
config CHARGER_BQ25890
tristate "TI BQ25890 battery charger driver"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 6c7da920ea83..8fcc175a7e22 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o
obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o
+obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o
obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c
new file mode 100644
index 000000000000..d9e3bb63667b
--- /dev/null
+++ b/drivers/power/supply/bq2515x_charger.c
@@ -0,0 +1,1167 @@
+// SPDX-License-Identifier: GPL-2.0
+// BQ2515X Battery Charger Driver
+// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define BQ2515X_MANUFACTURER "Texas Instruments"
+
+#define BQ2515X_STAT0 0x00
+#define BQ2515X_STAT1 0x01
+#define BQ2515X_STAT2 0x02
+#define BQ2515X_FLAG0 0x03
+#define BQ2515X_FLAG1 0x04
+#define BQ2515X_FLAG2 0x05
+#define BQ2515X_FLAG3 0x06
+#define BQ2515X_MASK0 0x07
+#define BQ2515X_MASK1 0x08
+#define BQ2515X_MASK2 0x09
+#define BQ2515X_MASK3 0x0a
+#define BQ2515X_VBAT_CTRL 0x12
+#define BQ2515X_ICHG_CTRL 0x13
+#define BQ2515X_PCHRGCTRL 0x14
+#define BQ2515X_TERMCTRL 0x15
+#define BQ2515X_BUVLO 0x16
+#define BQ2515X_CHARGERCTRL0 0x17
+#define BQ2515X_CHARGERCTRL1 0x18
+#define BQ2515X_ILIMCTRL 0x19
+#define BQ2515X_LDOCTRL 0x1d
+#define BQ2515X_MRCTRL 0x30
+#define BQ2515X_ICCTRL0 0x35
+#define BQ2515X_ICCTRL1 0x36
+#define BQ2515X_ICCTRL2 0x37
+#define BQ2515X_ADCCTRL0 0x40
+#define BQ2515X_ADCCTRL1 0x41
+#define BQ2515X_ADC_VBAT_M 0x42
+#define BQ2515X_ADC_VBAT_L 0x43
+#define BQ2515X_ADC_TS_M 0x44
+#define BQ2515X_ADC_TS_L 0x45
+#define BQ2515X_ADC_ICHG_M 0x46
+#define BQ2515X_ADC_ICHG_L 0x47
+#define BQ2515X_ADC_ADCIN_M 0x48
+#define BQ2515X_ADC_ADCIN_L 0x49
+#define BQ2515X_ADC_VIN_M 0x4a
+#define BQ2515X_ADC_VIN_L 0x4b
+#define BQ2515X_ADC_PMID_M 0x4c
+#define BQ2515X_ADC_PMID_L 0x4d
+#define BQ2515X_ADC_IIN_M 0x4e
+#define BQ2515X_ADC_IIN_L 0x4f
+#define BQ2515X_ADC_COMP1_M 0x52
+#define BQ2515X_ADC_COMP1_L 0X53
+#define BQ2515X_ADC_COMP2_M 0X54
+#define BQ2515X_ADC_COMP2_L 0x55
+#define BQ2515X_ADC_COMP3_M 0x56
+#define BQ2515X_ADC_COMP3_L 0x57
+#define BQ2515X_ADC_READ_EN 0x58
+#define BQ2515X_TS_FASTCHGCTRL 0x61
+#define BQ2515X_TS_COLD 0x62
+#define BQ2515X_TS_COOL 0x63
+#define BQ2515X_TS_WARM 0x64
+#define BQ2515X_TS_HOT 0x65
+#define BQ2515X_DEVICE_ID 0x6f
+
+#define BQ2515X_DEFAULT_ICHG_UA 10000
+#define BQ25150_DEFAULT_ILIM_UA 100000
+#define BQ25155_DEFAULT_ILIM_UA 500000
+#define BQ2515X_DEFAULT_VBAT_REG_UV 4200000
+#define BQ2515X_DEFAULT_IPRECHARGE_UA 2500
+
+#define BQ2515X_DIVISOR 65536
+#define BQ2515X_VBAT_BASE_VOLT 3600000
+#define BQ2515X_VBAT_REG_MAX 4600000
+#define BQ2515X_VBAT_REG_MIN 3600000
+#define BQ2515X_VBAT_STEP_UV 10000
+#define BQ2515X_UV_FACTOR 1000000
+#define BQ2515X_VBAT_MULTIPLIER 6
+#define BQ2515X_ICHG_DIVISOR 52429
+#define BQ2515X_ICHG_CURR_STEP_THRESH_UA 318750
+#define BQ2515X_ICHG_MIN_UA 0
+#define BQ2515X_ICHG_MAX_UA 500000
+#define BQ2515X_ICHG_RNG_1B0_UA 1250
+#define BQ2515X_ICHG_RNG_1B1_UA 2500
+#define BQ2515X_VLOWV_SEL_1B0_UV 3000000
+#define BQ2515X_VLOWV_SEL_1B1_UV 2800000
+#define BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA 18750
+#define BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA 37500
+#define BQ2515X_TWAKE2_MIN_US 1700000
+#define BQ2515X_TWAKE2_MAX_US 2300000
+
+#define BQ2515X_ILIM_150MA 0x2
+#define BQ2515X_ILIM_MASK 0x7
+#define BQ2515X_ILIM_MIN 50000
+#define BQ2515X_ILIM_MAX 600000
+#define BQ2515X_HEALTH_MASK 0xf
+#define BQ2515X_ICHGRNG_MASK 0x80
+#define BQ2515X_STAT0_MASK 0x0f
+#define BQ2515X_STAT1_MASK 0x1f
+#define BQ2515X_PRECHARGE_MASK 0x1f
+
+#define BQ2515X_TS_HOT_STAT BIT(0)
+#define BQ2515X_TS_WARM_STAT BIT(1)
+#define BQ2515X_TS_COOL_STAT BIT(2)
+#define BQ2515X_TS_COLD_STAT BIT(3)
+#define BQ2515X_SAFETY_TIMER_EXP BIT(5)
+
+#define BQ2515X_EN_VBAT_READ BIT(3)
+#define BQ2515X_EN_ICHG_READ BIT(5)
+
+#define BQ2515X_VIN_GOOD BIT(0)
+#define BQ2515X_CHRG_DONE BIT(5)
+#define BQ2515X_CV_CHRG_MODE BIT(6)
+
+#define BQ2515X_VIN_OVP_FAULT_STAT BIT(7)
+
+#define BQ2515X_WATCHDOG_DISABLE BIT(4)
+
+#define BQ2515X_ICHARGE_RANGE BIT(7)
+
+#define BQ2515X_VLOWV_SEL BIT(5)
+
+#define BQ2515X_CHARGER_DISABLE BIT(0)
+
+#define BQ2515X_HWRESET_14S_WD BIT(1)
+
+static const int bq2515x_ilim_lvl_values[] = {
+ 50000, 100000, 150000, 200000, 300000, 400000, 500000, 600000
+};
+
+/**
+ * struct bq2515x_init_data -
+ * @ilim: input current limit
+ */
+struct bq2515x_init_data {
+ int ilim;
+};
+
+enum bq2515x_id {
+ BQ25150,
+ BQ25155,
+};
+
+/**
+ * struct bq2515x_device -
+ * @mains: mains properties
+ * @battery: battery properties
+ * @regmap: register map structure
+ * @dev: device structure
+ *
+ * @reset_gpio: manual reset (MR) pin
+ * @lp_gpio: low power mode pin
+ * @ac_detect_gpio: power good (PG) pin
+ * @ce_gpio: charge enable (CE) pin
+ *
+ * @model_name: string value describing device model
+ * @device_id: value of device_id
+ * @mains_online: boolean value indicating power supply online
+ *
+ * @bq2515x_init_data init_data: charger initialization data structure
+ */
+struct bq2515x_device {
+ struct power_supply *mains;
+ struct power_supply *battery;
+ struct regmap *regmap;
+ struct device *dev;
+
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *lp_gpio;
+ struct gpio_desc *ac_detect_gpio;
+ struct gpio_desc *ce_gpio;
+
+ char model_name[I2C_NAME_SIZE];
+ int device_id;
+ bool mains_online;
+
+ struct bq2515x_init_data init_data;
+};
+
+static struct reg_default bq25150_reg_defaults[] = {
+ {BQ2515X_FLAG0, 0x0},
+ {BQ2515X_FLAG1, 0x0},
+ {BQ2515X_FLAG2, 0x0},
+ {BQ2515X_FLAG3, 0x0},
+ {BQ2515X_MASK0, 0x0},
+ {BQ2515X_MASK1, 0x0},
+ {BQ2515X_MASK2, 0x71},
+ {BQ2515X_MASK3, 0x0},
+ {BQ2515X_VBAT_CTRL, 0x3C},
+ {BQ2515X_ICHG_CTRL, 0x8},
+ {BQ2515X_PCHRGCTRL, 0x2},
+ {BQ2515X_TERMCTRL, 0x14},
+ {BQ2515X_BUVLO, 0x0},
+ {BQ2515X_CHARGERCTRL0, 0x82},
+ {BQ2515X_CHARGERCTRL1, 0x42},
+ {BQ2515X_ILIMCTRL, 0x1},
+ {BQ2515X_LDOCTRL, 0xB0},
+ {BQ2515X_MRCTRL, 0x2A},
+ {BQ2515X_ICCTRL0, 0x10},
+ {BQ2515X_ICCTRL1, 0x0},
+ {BQ2515X_ICCTRL2, 0x0},
+ {BQ2515X_ADCCTRL0, 0x2},
+ {BQ2515X_ADCCTRL1, 0x40},
+ {BQ2515X_ADC_COMP1_M, 0x23},
+ {BQ2515X_ADC_COMP1_L, 0x20},
+ {BQ2515X_ADC_COMP2_M, 0x38},
+ {BQ2515X_ADC_COMP2_L, 0x90},
+ {BQ2515X_ADC_COMP3_M, 0x0},
+ {BQ2515X_ADC_COMP3_L, 0x0},
+ {BQ2515X_ADC_READ_EN, 0x0},
+ {BQ2515X_TS_FASTCHGCTRL, 0x34},
+ {BQ2515X_TS_COLD, 0x7C},
+ {BQ2515X_TS_COOL, 0x6D},
+ {BQ2515X_TS_WARM, 0x38},
+ {BQ2515X_TS_HOT, 0x27},
+ {BQ2515X_DEVICE_ID, 0x20},
+};
+
+static struct reg_default bq25155_reg_defaults[] = {
+ {BQ2515X_FLAG0, 0x0},
+ {BQ2515X_FLAG1, 0x0},
+ {BQ2515X_FLAG2, 0x0},
+ {BQ2515X_FLAG3, 0x0},
+ {BQ2515X_MASK0, 0x0},
+ {BQ2515X_MASK1, 0x0},
+ {BQ2515X_MASK2, 0x71},
+ {BQ2515X_MASK3, 0x0},
+ {BQ2515X_VBAT_CTRL, 0x3C},
+ {BQ2515X_ICHG_CTRL, 0x8},
+ {BQ2515X_PCHRGCTRL, 0x2},
+ {BQ2515X_TERMCTRL, 0x14},
+ {BQ2515X_BUVLO, 0x0},
+ {BQ2515X_CHARGERCTRL0, 0x82},
+ {BQ2515X_CHARGERCTRL1, 0xC2},
+ {BQ2515X_ILIMCTRL, 0x6},
+ {BQ2515X_LDOCTRL, 0xB0},
+ {BQ2515X_MRCTRL, 0x2A},
+ {BQ2515X_ICCTRL0, 0x10},
+ {BQ2515X_ICCTRL1, 0x0},
+ {BQ2515X_ICCTRL2, 0x40},
+ {BQ2515X_ADCCTRL0, 0x2},
+ {BQ2515X_ADCCTRL1, 0x40},
+ {BQ2515X_ADC_COMP1_M, 0x23},
+ {BQ2515X_ADC_COMP1_L, 0x20},
+ {BQ2515X_ADC_COMP2_M, 0x38},
+ {BQ2515X_ADC_COMP2_L, 0x90},
+ {BQ2515X_ADC_COMP3_M, 0x0},
+ {BQ2515X_ADC_COMP3_L, 0x0},
+ {BQ2515X_ADC_READ_EN, 0x0},
+ {BQ2515X_TS_FASTCHGCTRL, 0x34},
+ {BQ2515X_TS_COLD, 0x7C},
+ {BQ2515X_TS_COOL, 0x6D},
+ {BQ2515X_TS_WARM, 0x38},
+ {BQ2515X_TS_HOT, 0x27},
+ {BQ2515X_DEVICE_ID, 0x35},
+};
+
+static int bq2515x_wake_up(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ int val;
+
+ /* Read the STAT register if we can read it then the device is out
+ * of ship mode. If the register cannot be read then attempt to wake
+ * it up and enable the ADC.
+ */
+ ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val);
+ if (ret)
+ return ret;
+
+ /* Need to toggle LP and bring device out of ship mode. The device
+ * will exit the ship mode when the MR pin is held low for at least
+ * t_WAKE2 as shown in section 8.3.7.1 of the datasheet.
+ */
+ gpiod_set_value_cansleep(bq2515x->lp_gpio, 0);
+
+ gpiod_set_value_cansleep(bq2515x->reset_gpio, 0);
+ usleep_range(BQ2515X_TWAKE2_MIN_US, BQ2515X_TWAKE2_MAX_US);
+ gpiod_set_value_cansleep(bq2515x->reset_gpio, 1);
+
+ return regmap_write(bq2515x->regmap, BQ2515X_ADC_READ_EN,
+ (BQ2515X_EN_VBAT_READ | BQ2515X_EN_ICHG_READ));
+}
+
+static int bq2515x_update_ps_status(struct bq2515x_device *bq2515x)
+{
+ bool dc = false;
+ unsigned int val;
+ int ret;
+
+ if (bq2515x->ac_detect_gpio)
+ val = gpiod_get_value_cansleep(bq2515x->ac_detect_gpio);
+ else {
+ ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val);
+ if (ret)
+ return ret;
+ }
+
+ dc = val & BQ2515X_VIN_GOOD;
+
+ ret = bq2515x->mains_online != dc;
+
+ bq2515x->mains_online = dc;
+
+ return ret;
+}
+
+static int bq2515x_disable_watchdog_timers(struct bq2515x_device *bq2515x)
+{
+ int ret;
+
+ ret = regmap_update_bits(bq2515x->regmap, BQ2515X_CHARGERCTRL0,
+ BQ2515X_WATCHDOG_DISABLE, BQ2515X_WATCHDOG_DISABLE);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2,
+ BQ2515X_HWRESET_14S_WD, 0);
+}
+
+static int bq2515x_get_battery_voltage_now(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ int vbat_msb;
+ int vbat_lsb;
+ uint32_t vbat_measurement;
+
+ if (!bq2515x->mains_online)
+ bq2515x_wake_up(bq2515x);
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_M, &vbat_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_L, &vbat_lsb);
+ if (ret)
+ return ret;
+
+ vbat_measurement = (vbat_msb << 8) | vbat_lsb;
+
+ return vbat_measurement * (BQ2515X_UV_FACTOR / BQ2515X_DIVISOR) *
+ BQ2515X_VBAT_MULTIPLIER;
+}
+
+static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ int ichg_msb;
+ int ichg_lsb;
+ uint32_t ichg_measurement;
+ u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+ unsigned int ichg_reg_code, reg_code;
+ unsigned int icharge_range = 0, pchrgctrl;
+ unsigned int buvlo, vlowv_sel, vlowv = BQ2515X_VLOWV_SEL_1B0_UV;
+
+ if (!bq2515x->mains_online)
+ return -ENODATA;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_M, &ichg_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_L, &ichg_lsb);
+ if (ret)
+ return ret;
+
+ ichg_measurement = (ichg_msb << 8) | ichg_lsb;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_BUVLO, &buvlo);
+ if (ret)
+ return ret;
+
+ vlowv_sel = buvlo & BQ2515X_VLOWV_SEL;
+
+ if (vlowv_sel)
+ vlowv = BQ2515X_VLOWV_SEL_1B1_UV;
+
+ if (bq2515x_get_battery_voltage_now(bq2515x) < vlowv) {
+ ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL,
+ &pchrgctrl);
+ if (ret)
+ return ret;
+
+ reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK;
+ } else {
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL,
+ &ichg_reg_code);
+ if (ret)
+ return ret;
+
+ reg_code = ichg_reg_code;
+ }
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+ if (ret)
+ return ret;
+
+ icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+ if (icharge_range)
+ ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+
+ return reg_code * (ichg_multiplier * ichg_measurement /
+ BQ2515X_ICHG_DIVISOR);
+}
+
+static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ int ce_pin;
+ int icctrl2;
+ int charger_disable;
+
+ ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio);
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2);
+ if (ret)
+ return ret;
+
+ charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE;
+
+ if (charger_disable || ce_pin)
+ return true;
+
+ return false;
+}
+
+static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val)
+{
+ gpiod_set_value_cansleep(bq2515x->ce_gpio, val);
+
+ return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2,
+ BQ2515X_CHARGER_DISABLE, val);
+}
+
+static int bq2515x_get_const_charge_current(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+ unsigned int ichg_reg_code;
+ unsigned int pchrgctrl;
+ unsigned int icharge_range;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, &ichg_reg_code);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+ if (ret)
+ return ret;
+
+ icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+ if (icharge_range)
+ ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+
+ return ichg_reg_code * ichg_multiplier;
+}
+
+static int bq2515x_set_const_charge_current(struct bq2515x_device *bq2515x,
+ int val)
+{
+ int ret;
+ unsigned int ichg_reg_code;
+ u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+ unsigned int icharge_range = 0;
+
+ if (val > BQ2515X_ICHG_MAX_UA || val < BQ2515X_ICHG_MIN_UA)
+ return -EINVAL;
+
+ if (val > BQ2515X_ICHG_CURR_STEP_THRESH_UA) {
+ ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+ icharge_range = BQ2515X_ICHARGE_RANGE;
+ }
+
+ bq2515x_set_charge_disable(bq2515x, 1);
+
+ ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL,
+ BQ2515X_ICHARGE_RANGE, icharge_range);
+ if (ret)
+ return ret;
+
+ ichg_reg_code = val / ichg_multiplier;
+
+ ret = regmap_write(bq2515x->regmap, BQ2515X_ICHG_CTRL, ichg_reg_code);
+ if (ret)
+ return ret;
+
+ return bq2515x_set_charge_disable(bq2515x, 0);
+}
+
+static int bq2515x_get_precharge_current(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ unsigned int pchrgctrl;
+ unsigned int icharge_range;
+ u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+ unsigned int precharge_reg_code;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+ if (ret)
+ return ret;
+
+ icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+ if (icharge_range)
+ precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+
+ precharge_reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK;
+
+ return precharge_reg_code * precharge_multiplier;
+}
+
+static int bq2515x_set_precharge_current(struct bq2515x_device *bq2515x,
+ int val)
+{
+ int ret;
+ unsigned int pchrgctrl;
+ unsigned int icharge_range;
+ unsigned int precharge_reg_code;
+ u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA;
+ u16 precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl);
+ if (ret)
+ return ret;
+
+ icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE;
+
+ if (icharge_range) {
+ precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA;
+ precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA;
+ }
+ if (val > precharge_max_ua || val < BQ2515X_ICHG_MIN_UA)
+ return -EINVAL;
+
+ precharge_reg_code = val / precharge_multiplier;
+
+ ret = bq2515x_set_charge_disable(bq2515x, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL,
+ BQ2515X_PRECHARGE_MASK, precharge_reg_code);
+ if (ret)
+ return ret;
+
+ return bq2515x_set_charge_disable(bq2515x, 0);
+}
+
+static int bq2515x_charging_status(struct bq2515x_device *bq2515x,
+ union power_supply_propval *val)
+{
+ bool status0_no_fault;
+ bool status1_no_fault;
+ bool ce_status;
+ bool charge_done;
+ unsigned int status;
+ int ret;
+
+ if (!bq2515x->mains_online) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ return 0;
+ }
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &status);
+ if (ret)
+ return ret;
+
+ /*
+ * The code block below is used to determine if any faults from the
+ * STAT0 register are disbaling charging or if the charge has completed
+ * according to the CHARGE_DONE_STAT bit.
+ */
+ if (((status & BQ2515X_STAT0_MASK) == true) &
+ ((status & BQ2515X_CHRG_DONE) == false)) {
+ status0_no_fault = true;
+ charge_done = false;
+ } else if (status & BQ2515X_CHRG_DONE) {
+ charge_done = true;
+ status0_no_fault = false;
+ } else {
+ status0_no_fault = false;
+ charge_done = false;
+ }
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &status);
+ if (ret)
+ return ret;
+ /*
+ * The code block below is used to determine if any faults from the
+ * STAT1 register are disbaling charging
+ */
+ if ((status & BQ2515X_STAT1_MASK) == false)
+ status1_no_fault = true;
+ else
+ status1_no_fault = false;
+
+ ce_status = (!bq2515x_get_charge_disable(bq2515x));
+
+ /*
+ * If there are no faults and charging is enabled, then status is
+ * charging. Otherwise, if charging is complete, then status is full.
+ * Otherwise, if a fault exists or charging is disabled, then status is
+ * not charging
+ */
+ if (status0_no_fault & status1_no_fault & ce_status)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (charge_done)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else if (!(status0_no_fault & status1_no_fault & ce_status))
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ return 0;
+}
+
+static int bq2515x_get_batt_reg(struct bq2515x_device *bq2515x)
+{
+ int vbat_reg_code;
+ int ret;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_VBAT_CTRL, &vbat_reg_code);
+ if (ret)
+ return ret;
+
+ return BQ2515X_VBAT_BASE_VOLT + vbat_reg_code * BQ2515X_VBAT_STEP_UV;
+}
+
+static int bq2515x_set_batt_reg(struct bq2515x_device *bq2515x, int val)
+{
+ int vbat_reg_code;
+
+ if (val > BQ2515X_VBAT_REG_MAX || val < BQ2515X_VBAT_REG_MIN)
+ return -EINVAL;
+
+ vbat_reg_code = (val - BQ2515X_VBAT_BASE_VOLT) / BQ2515X_VBAT_STEP_UV;
+
+ return regmap_write(bq2515x->regmap, BQ2515X_VBAT_CTRL, vbat_reg_code);
+}
+
+static int bq2515x_get_ilim_lvl(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ int ilimctrl;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_ILIMCTRL, &ilimctrl);
+ if (ret)
+ return ret;
+
+ return bq2515x_ilim_lvl_values[ilimctrl & BQ2515X_ILIM_MASK];
+}
+
+static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val)
+{
+ int i;
+
+ if (val > BQ2515X_ILIM_MAX || val < BQ2515X_ILIM_MIN)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(bq2515x_ilim_lvl_values); i++) {
+ if (val == bq2515x_ilim_lvl_values[i])
+ break;
+
+ if (val > bq2515x_ilim_lvl_values[i - 1] &&
+ val < bq2515x_ilim_lvl_values[i]) {
+ if (val - bq2515x_ilim_lvl_values[i - 1] <
+ bq2515x_ilim_lvl_values[i] - val) {
+ i = i - 1;
+ break;
+ }
+ }
+ }
+
+ return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i);
+}
+
+static int bq2515x_power_supply_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int bq2515x_charger_get_health(struct bq2515x_device *bq2515x,
+ union power_supply_propval *val)
+{
+ int health = POWER_SUPPLY_HEALTH_GOOD;
+ int ret;
+ unsigned int stat1;
+ unsigned int flag3;
+
+ if (!bq2515x->mains_online)
+ bq2515x_wake_up(bq2515x);
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_FLAG3, &flag3);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &stat1);
+ if (ret)
+ return ret;
+
+ if (stat1 & BQ2515X_HEALTH_MASK) {
+ switch (stat1 & BQ2515X_HEALTH_MASK) {
+ case BQ2515X_TS_HOT_STAT:
+ health = POWER_SUPPLY_HEALTH_HOT;
+ break;
+ case BQ2515X_TS_WARM_STAT:
+ health = POWER_SUPPLY_HEALTH_WARM;
+ break;
+ case BQ2515X_TS_COOL_STAT:
+ health = POWER_SUPPLY_HEALTH_COOL;
+ break;
+ case BQ2515X_TS_COLD_STAT:
+ health = POWER_SUPPLY_HEALTH_COLD;
+ break;
+ default:
+ health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ break;
+ }
+ }
+
+ if (stat1 & BQ2515X_VIN_OVP_FAULT_STAT)
+ health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+
+ if (flag3 & BQ2515X_SAFETY_TIMER_EXP)
+ health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+
+ val->intval = health;
+ return 0;
+}
+
+static int bq2515x_mains_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+ int ret;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = bq2515x_set_ilim_lvl(bq2515x, val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int bq2515x_battery_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+ int ret;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = bq2515x_set_batt_reg(bq2515x, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = bq2515x_set_const_charge_current(bq2515x, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ ret = bq2515x_set_precharge_current(bq2515x, val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int bq2515x_mains_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = bq2515x->mains_online;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = bq2515x_charger_get_health(bq2515x, val);
+ if (ret)
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = bq2515x_get_ilim_lvl(bq2515x);
+ if (ret)
+ return ret;
+
+ val->intval = ret;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = bq2515x->model_name;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = BQ2515X_MANUFACTURER;
+ break;
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = bq2515x_charging_status(bq2515x, val);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int bq2515x_battery_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy);
+ int ret;
+
+ ret = bq2515x_update_ps_status(bq2515x);
+ if (ret)
+ return ret;
+
+ switch (prop) {
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = bq2515x_get_battery_voltage_now(bq2515x);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = bq2515x_get_battery_current_now(bq2515x);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = bq2515x_get_const_charge_current(bq2515x);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+ case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+ ret = bq2515x_get_precharge_current(bq2515x);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = bq2515x_get_batt_reg(bq2515x);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property bq2515x_battery_properties[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+};
+
+static enum power_supply_property bq2515x_charger_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static struct power_supply_desc bq2515x_mains_desc = {
+ .name = "bq2515x-mains",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .get_property = bq2515x_mains_get_property,
+ .set_property = bq2515x_mains_set_property,
+ .properties = bq2515x_charger_properties,
+ .num_properties = ARRAY_SIZE(bq2515x_charger_properties),
+ .property_is_writeable = bq2515x_power_supply_property_is_writeable,
+};
+
+static struct power_supply_desc bq2515x_battery_desc = {
+ .name = "bq2515x-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .get_property = bq2515x_battery_get_property,
+ .set_property = bq2515x_battery_set_property,
+ .properties = bq2515x_battery_properties,
+ .num_properties = ARRAY_SIZE(bq2515x_battery_properties),
+ .property_is_writeable = bq2515x_power_supply_property_is_writeable,
+};
+
+static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x,
+ struct device *dev, struct power_supply_config psy_cfg)
+{
+ bq2515x->mains = devm_power_supply_register(bq2515x->dev,
+ &bq2515x_mains_desc,
+ &psy_cfg);
+ if (IS_ERR(bq2515x->mains))
+ return -EINVAL;
+
+ bq2515x->battery = devm_power_supply_register(bq2515x->dev,
+ &bq2515x_battery_desc,
+ &psy_cfg);
+ if (IS_ERR(bq2515x->battery))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bq2515x_hw_init(struct bq2515x_device *bq2515x)
+{
+ int ret;
+ struct power_supply_battery_info bat_info = { };
+
+ ret = bq2515x_disable_watchdog_timers(bq2515x);
+ if (ret)
+ return ret;
+
+ if (bq2515x->init_data.ilim) {
+ ret = bq2515x_set_ilim_lvl(bq2515x, bq2515x->init_data.ilim);
+ if (ret)
+ return ret;
+ }
+
+ ret = power_supply_get_battery_info(bq2515x->mains, &bat_info);
+ if (ret) {
+ dev_warn(bq2515x->dev, "battery info missing, default values will be applied\n");
+
+ bat_info.constant_charge_current_max_ua =
+ BQ2515X_DEFAULT_ICHG_UA;
+
+ bat_info.constant_charge_voltage_max_uv =
+ BQ2515X_DEFAULT_VBAT_REG_UV;
+
+ bat_info.precharge_current_ua =
+ BQ2515X_DEFAULT_IPRECHARGE_UA;
+ }
+
+ ret = bq2515x_set_const_charge_current(bq2515x,
+ bat_info.constant_charge_current_max_ua);
+ if (ret)
+ return ret;
+
+ ret = bq2515x_set_batt_reg(bq2515x,
+ bat_info.constant_charge_voltage_max_uv);
+ if (ret)
+ return ret;
+
+ return bq2515x_set_precharge_current(bq2515x,
+ bat_info.precharge_current_ua);
+}
+
+static int bq2515x_read_properties(struct bq2515x_device *bq2515x)
+{
+ int ret;
+
+ ret = device_property_read_u32(bq2515x->dev,
+ "input-current-limit-microamp",
+ &bq2515x->init_data.ilim);
+ if (ret)
+ switch (bq2515x->device_id) {
+ case BQ25150:
+ bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA;
+ break;
+ case BQ25155:
+ bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA;
+ break;
+ }
+
+ bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev,
+ "ac-detect", GPIOD_IN);
+ if (IS_ERR(bq2515x->ac_detect_gpio)) {
+ ret = PTR_ERR(bq2515x->ac_detect_gpio);
+ if (ret != -ENODEV) {
+ dev_err(bq2515x->dev, "Failed to get ac detect");
+ return ret;
+ }
+ bq2515x->ac_detect_gpio = NULL;
+ }
+
+ bq2515x->reset_gpio = devm_gpiod_get_optional(bq2515x->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(bq2515x->reset_gpio)) {
+ ret = PTR_ERR(bq2515x->reset_gpio);
+ if (ret != -ENODEV) {
+ dev_err(bq2515x->dev, "Failed to get reset");
+ return ret;
+ }
+ bq2515x->reset_gpio = NULL;
+ }
+
+ bq2515x->lp_gpio = devm_gpiod_get_optional(bq2515x->dev, "low-power",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(bq2515x->lp_gpio)) {
+ ret = PTR_ERR(bq2515x->lp_gpio);
+ if (ret != -ENODEV) {
+ dev_err(bq2515x->dev, "Failed to get low power");
+ return ret;
+ }
+ bq2515x->lp_gpio = NULL;
+ }
+
+ bq2515x->ce_gpio = devm_gpiod_get_optional(bq2515x->dev,
+ "charge-enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(bq2515x->ce_gpio)) {
+ ret = PTR_ERR(bq2515x->ce_gpio);
+ if (ret != -ENODEV) {
+ dev_err(bq2515x->dev, "Failed to get ce");
+ return ret;
+ }
+ bq2515x->ce_gpio = NULL;
+ }
+
+ return 0;
+}
+
+static bool bq2515x_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BQ2515X_STAT0 ... BQ2515X_FLAG3:
+ case BQ2515X_ADC_VBAT_M ... BQ2515X_ADC_IIN_L:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config bq25150_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BQ2515X_DEVICE_ID,
+ .reg_defaults = bq25150_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(bq25150_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = bq2515x_volatile_register,
+};
+
+static const struct regmap_config bq25155_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BQ2515X_DEVICE_ID,
+ .reg_defaults = bq25155_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(bq25155_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = bq2515x_volatile_register,
+};
+
+static int bq2515x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct bq2515x_device *bq2515x;
+ struct power_supply_config charger_cfg = {};
+ int ret;
+
+ bq2515x = devm_kzalloc(dev, sizeof(*bq2515x), GFP_KERNEL);
+ if (!bq2515x)
+ return -ENOMEM;
+
+ bq2515x->dev = dev;
+
+ strncpy(bq2515x->model_name, id->name, I2C_NAME_SIZE);
+
+ bq2515x->device_id = id->driver_data;
+
+ switch (bq2515x->device_id) {
+ case BQ25150:
+ bq2515x->regmap = devm_regmap_init_i2c(client,
+ &bq25150_regmap_config);
+ break;
+ case BQ25155:
+ bq2515x->regmap = devm_regmap_init_i2c(client,
+ &bq25155_regmap_config);
+ break;
+ }
+
+ if (IS_ERR(bq2515x->regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(bq2515x->regmap);
+ }
+
+ i2c_set_clientdata(client, bq2515x);
+
+ charger_cfg.drv_data = bq2515x;
+ charger_cfg.of_node = dev->of_node;
+
+ ret = bq2515x_read_properties(bq2515x);
+ if (ret) {
+ dev_err(dev, "Failed to read device tree properties %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = bq2515x_power_supply_register(bq2515x, dev, charger_cfg);
+ if (ret) {
+ dev_err(dev, "failed to register power supply\n");
+ return ret;
+ }
+
+ ret = bq2515x_hw_init(bq2515x);
+ if (ret) {
+ dev_err(dev, "Cannot initialize the chip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id bq2515x_i2c_ids[] = {
+ { "bq25150", BQ25150, },
+ { "bq25155", BQ25155, },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids);
+
+static const struct of_device_id bq2515x_of_match[] = {
+ { .compatible = "ti,bq25150", },
+ { .compatible = "ti,bq25155", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bq2515x_of_match);
+
+static struct i2c_driver bq2515x_driver = {
+ .driver = {
+ .name = "bq2515x-charger",
+ .of_match_table = bq2515x_of_match,
+ },
+ .probe = bq2515x_probe,
+ .id_table = bq2515x_i2c_ids,
+};
+module_i2c_driver(bq2515x_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
+MODULE_DESCRIPTION("BQ2515X charger driver");
+MODULE_LICENSE("GPL v2");
--
2.26.2
^ permalink raw reply related
* [PATCH v7 1/3] power_supply: Add additional health properties to the header
From: Dan Murphy @ 2020-05-19 14:19 UTC (permalink / raw)
To: sre
Cc: sspatil, linux-pm, robh, linux-kernel, devicetree, Dan Murphy,
Guru Das Srinagesh
In-Reply-To: <20200519141916.28282-1-dmurphy@ti.com>
Add HEALTH_WARM, HEALTH_COOL and HEALTH_HOT to the health enum.
HEALTH_WARM, HEALTH_COOL, and HEALTH_HOT properties are taken from the JEITA spec.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
Tested-by: Guru Das Srinagesh <gurus@codeaurora.org>
---
Documentation/ABI/testing/sysfs-class-power | 2 +-
drivers/power/supply/power_supply_sysfs.c | 2 +-
include/linux/power_supply.h | 3 +++
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
index bf3b48f022dc..9f3fd01a9373 100644
--- a/Documentation/ABI/testing/sysfs-class-power
+++ b/Documentation/ABI/testing/sysfs-class-power
@@ -190,7 +190,7 @@ Description:
Valid values: "Unknown", "Good", "Overheat", "Dead",
"Over voltage", "Unspecified failure", "Cold",
"Watchdog timer expire", "Safety timer expire",
- "Over current"
+ "Over current", "Warm", "Cool", "Hot"
What: /sys/class/power_supply/<supply_name>/precharge_current
Date: June 2017
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index f37ad4eae60b..d0d549611794 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -61,7 +61,7 @@ static const char * const power_supply_charge_type_text[] = {
static const char * const power_supply_health_text[] = {
"Unknown", "Good", "Overheat", "Dead", "Over voltage",
"Unspecified failure", "Cold", "Watchdog timer expire",
- "Safety timer expire", "Over current"
+ "Safety timer expire", "Over current", "Warm", "Cool", "Hot"
};
static const char * const power_supply_technology_text[] = {
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index dcd5a71e6c67..8670e90c1d51 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -61,6 +61,9 @@ enum {
POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
POWER_SUPPLY_HEALTH_OVERCURRENT,
+ POWER_SUPPLY_HEALTH_WARM,
+ POWER_SUPPLY_HEALTH_COOL,
+ POWER_SUPPLY_HEALTH_HOT,
};
enum {
--
2.26.2
^ permalink raw reply related
* [PATCH v7 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments.
From: Dan Murphy @ 2020-05-19 14:19 UTC (permalink / raw)
To: sre; +Cc: sspatil, linux-pm, robh, linux-kernel, devicetree,
Ricardo Rivera-Matos
In-Reply-To: <20200519141916.28282-1-dmurphy@ti.com>
From: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
The BQ2515X family of devices are highly integrated battery management
ICs that integrate the most common functions for wearable devices
namely a charger, an output voltage rail, ADC for battery and system
monitoring, and a push-button controller.
Datasheets:
http://www.ti.com/lit/ds/symlink/bq25150.pdf
http://www.ti.com/lit/ds/symlink/bq25155.pdf
Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
---
.../bindings/power/supply/bq2515x.yaml | 106 ++++++++++++++++++
1 file changed, 106 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml
new file mode 100644
index 000000000000..83487957fc8c
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml
@@ -0,0 +1,106 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: TI bq2515x 500-mA Linear charger family
+
+maintainers:
+ - Dan Murphy <dmurphy@ti.com>
+ - Ricardo Rivera-Matos <r-rivera-matos@ti.com>
+
+description: |
+ The BQ2515x family is a highly integrated battery charge management IC that
+ integrates the most common functions for wearable devices, namely a charger,
+ an output voltage rail, ADC for battery and system monitoring, and
+ push-button controller.
+
+ Specifications about the charger can be found at:
+ http://www.ti.com/lit/ds/symlink/bq25150.pdf
+ http://www.ti.com/lit/ds/symlink/bq25155.pdf
+
+properties:
+ compatible:
+ enum:
+ - ti,bq25150
+ - ti,bq25155
+
+ reg:
+ maxItems: 1
+ description: I2C address of the charger.
+
+ ac-detect-gpios:
+ description: |
+ GPIO used for connecting the bq2515x device PG (AC Detect)
+ pin. This pin should be used if possible as this is the
+ recommended way to obtain the charger's input PG state.
+ If this pin is not specified a software-based approach for PG
+ detection is used.
+
+ reset-gpios:
+ description: |
+ GPIO used for hardware reset.
+
+ low-power-gpios:
+ description: |
+ GPIO used for low power mode of IC.
+
+ charge-enable-gpios:
+ description: |
+ GPIO used to turn on and off charging.
+
+ constant-charge-current-max-microamp:
+ description: |
+ Maximum charging current in micro Amps.
+ minimum: 50000
+ maximum: 600000
+
+ precharge-current-max-microamp:
+ description: |
+ Maximum precharging current in micro Amps.
+ minimum: 1250
+ maximum: 77500
+
+ input-current-limit-microamp:
+ description: |
+ Maximum input current in micro Amps.
+ minimum: 50000
+ maximum: 500000
+
+ constant-charge-voltage-max-microvolt:
+ description: |
+ Maximum charging voltage in micro volts.
+ minimum: 3600000
+ maximum: 4600000
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ bat: battery {
+ compatible = "simple-battery";
+ constant-charge-current-max-microamp = <50000>;
+ precharge-current-microamp = <2500>;
+ constant-charge-voltage-max-microvolt = <4000000>;
+ };
+ #include <dt-bindings/gpio/gpio.h>
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bq25150: charger@6b {
+ compatible = "ti,bq25150";
+ reg = <0x6b>;
+ monitored-battery = <&bat>;
+ input-current-limit-microamp = <100000>;
+
+ ac-detect-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+ low-power-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
+ charge-enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+ };
+ };
--
2.26.2
^ permalink raw reply related
* Re: [PATCH v6] ARM: boot: Obtain start of physical memory from DTB
From: Russell King - ARM Linux admin @ 2020-05-19 14:28 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: Geert Uytterhoeven, Lukasz Stelmach, Dmitry Osipenko,
Nicolas Pitre, Arnd Bergmann, Eric Miao, Uwe Kleine-König,
Masahiro Yamada, Marek Szyprowski, Chris Brandt, Linux ARM,
Linux-Renesas, Linux Kernel Mailing List,
Bartlomiej Zolnierkiewicz,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Rob Herring, Grant Likely
In-Reply-To: <CAMj1kXH_s4qjDfTO03PkGNaiwfjmfkrZ-FE8vTm74QSrgoVt0A@mail.gmail.com>
On Tue, May 19, 2020 at 03:56:59PM +0200, Ard Biesheuvel wrote:
> On Tue, 19 May 2020 at 13:21, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> >
> > Hi Russell,
> >
> > CC devicetree
> >
> > On Tue, May 19, 2020 at 11:46 AM Russell King - ARM Linux admin
> > <linux@armlinux.org.uk> wrote:
> > > On Tue, May 19, 2020 at 11:44:17AM +0200, Geert Uytterhoeven wrote:
> > > > On Tue, May 19, 2020 at 10:54 AM Lukasz Stelmach <l.stelmach@samsung.com> wrote:
> > > > > It was <2020-04-29 śro 10:21>, when Geert Uytterhoeven wrote:
> > > > > > Currently, the start address of physical memory is obtained by masking
> > > > > > the program counter with a fixed mask of 0xf8000000. This mask value
> > > > > > was chosen as a balance between the requirements of different platforms.
> > > > > > However, this does require that the start address of physical memory is
> > > > > > a multiple of 128 MiB, precluding booting Linux on platforms where this
> > > > > > requirement is not fulfilled.
> > > > > >
> > > > > > Fix this limitation by obtaining the start address from the DTB instead,
> > > > > > if available (either explicitly passed, or appended to the kernel).
> > > > > > Fall back to the traditional method when needed.
> > > > > >
> > > > > > This allows to boot Linux on r7s9210/rza2mevb using the 64 MiB of SDRAM
> > > > > > on the RZA2MEVB sub board, which is located at 0x0C000000 (CS3 space),
> > > > > > i.e. not at a multiple of 128 MiB.
> > > > > >
> > > > > > Suggested-by: Nicolas Pitre <nico@fluxnic.net>
> > > > > > Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > > > > > Reviewed-by: Nicolas Pitre <nico@fluxnic.net>
> > > > > > Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
> > > > > > Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> > > > > > Tested-by: Dmitry Osipenko <digetx@gmail.com>
> > > > > > ---
> > > > >
> > > > > [...]
> > > > >
> > > > > Apparently reading physical memory layout from DTB breaks crashdump
> > > > > kernels. A crashdump kernel is loaded into a region of memory, that is
> > > > > reserved in the original (i.e. to be crashed) kernel. The reserved
> > > > > region is large enough for the crashdump kernel to run completely inside
> > > > > it and don't modify anything outside it, just read and dump the remains
> > > > > of the crashed kernel. Using the information from DTB makes the
> > > > > decompressor place the kernel outside of the dedicated region.
> > > > >
> > > > > The log below shows that a zImage and DTB are loaded at 0x18eb8000 and
> > > > > 0x193f6000 (physical). The kernel is expected to run at 0x18008000, but
> > > > > it is decompressed to 0x00008000 (see r4 reported before jumping from
> > > > > within __enter_kernel). If I were to suggest something, there need to be
> > > > > one more bit of information passed in the DTB telling the decompressor
> > > > > to use the old masking technique to determain kernel address. It would
> > > > > be set in the DTB loaded along with the crashdump kernel.
> > > >
> > > > Shouldn't the DTB passed to the crashkernel describe which region of
> > > > memory is to be used instead?
> > >
> > > Definitely not. The crashkernel needs to know where the RAM in the
> > > machine is, so that it can create a coredump of the crashed kernel.
> >
> > So the DTB should describe both ;-)
> >
> > > > Describing "to use the old masking technique" sounds a bit hackish to me.
> > > > I guess it cannot just restrict the /memory node to the reserved region,
> > > > as the crashkernel needs to be able to dump the remains of the crashed
> > > > kernel, which lie outside this region.
> > >
> > > Correct.
> > >
> > > > However, something under /chosen should work.
> > >
> > > Yet another sticky plaster...
> >
> > IMHO the old masking technique is the hacky solution covered by
> > plasters.
> >
>
> I think debating which solution is the hacky one will not get us anywhere.
>
> The simple reality is that the existing solution works fine for
> existing platforms, and so any changes in the logic will have to be
> opt-in in one way or the other.
>
> Since U-boot supports EFI boot these days, one potential option is to
> rely on that. I have some changes implementing this that go on top of
> this patch, but they don't actually rely on it - it was just to
> prevent lexical conflicts.
>
> The only remaining options imo are a kernel command line option, or a
> DT property that tells the decompressor to look at the memory nodes.
> But using the DT memory nodes on all platforms like this patch does is
> obviously just too risky.
>
> On another note, I do think the usable-memory-region property should
> be implemented for ARM as well - relying on this rounding to ensure
> that the decompressor does the right thing is too fragile.
What is "too fragile" is trying to change this and expecting everything
to continue working as it did before.
How will switching to EFI help? Won't the crashdump kernel detect EFI
and try to get the memory map from EFI, whereby it runs into exactly
the same issue that the DT approach does?
The current crashkernel situation works precisely because of the 128M
masking that is being done.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC for 0.8m (est. 1762m) line in suburbia: sync at 13.1Mbps down 424kbps up
^ permalink raw reply
* Re: [PATCH v6] ARM: boot: Obtain start of physical memory from DTB
From: Ard Biesheuvel @ 2020-05-19 14:32 UTC (permalink / raw)
To: Russell King - ARM Linux admin
Cc: Geert Uytterhoeven, Lukasz Stelmach, Dmitry Osipenko,
Nicolas Pitre, Arnd Bergmann, Eric Miao, Uwe Kleine-König,
Masahiro Yamada, Marek Szyprowski, Chris Brandt, Linux ARM,
Linux-Renesas, Linux Kernel Mailing List,
Bartlomiej Zolnierkiewicz,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Rob Herring, Grant Likely
In-Reply-To: <20200519142854.GF1551@shell.armlinux.org.uk>
On Tue, 19 May 2020 at 16:29, Russell King - ARM Linux admin
<linux@armlinux.org.uk> wrote:
>
> On Tue, May 19, 2020 at 03:56:59PM +0200, Ard Biesheuvel wrote:
> > On Tue, 19 May 2020 at 13:21, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> > >
> > > Hi Russell,
> > >
> > > CC devicetree
> > >
> > > On Tue, May 19, 2020 at 11:46 AM Russell King - ARM Linux admin
> > > <linux@armlinux.org.uk> wrote:
> > > > On Tue, May 19, 2020 at 11:44:17AM +0200, Geert Uytterhoeven wrote:
> > > > > On Tue, May 19, 2020 at 10:54 AM Lukasz Stelmach <l.stelmach@samsung.com> wrote:
> > > > > > It was <2020-04-29 śro 10:21>, when Geert Uytterhoeven wrote:
> > > > > > > Currently, the start address of physical memory is obtained by masking
> > > > > > > the program counter with a fixed mask of 0xf8000000. This mask value
> > > > > > > was chosen as a balance between the requirements of different platforms.
> > > > > > > However, this does require that the start address of physical memory is
> > > > > > > a multiple of 128 MiB, precluding booting Linux on platforms where this
> > > > > > > requirement is not fulfilled.
> > > > > > >
> > > > > > > Fix this limitation by obtaining the start address from the DTB instead,
> > > > > > > if available (either explicitly passed, or appended to the kernel).
> > > > > > > Fall back to the traditional method when needed.
> > > > > > >
> > > > > > > This allows to boot Linux on r7s9210/rza2mevb using the 64 MiB of SDRAM
> > > > > > > on the RZA2MEVB sub board, which is located at 0x0C000000 (CS3 space),
> > > > > > > i.e. not at a multiple of 128 MiB.
> > > > > > >
> > > > > > > Suggested-by: Nicolas Pitre <nico@fluxnic.net>
> > > > > > > Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > > > > > > Reviewed-by: Nicolas Pitre <nico@fluxnic.net>
> > > > > > > Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
> > > > > > > Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> > > > > > > Tested-by: Dmitry Osipenko <digetx@gmail.com>
> > > > > > > ---
> > > > > >
> > > > > > [...]
> > > > > >
> > > > > > Apparently reading physical memory layout from DTB breaks crashdump
> > > > > > kernels. A crashdump kernel is loaded into a region of memory, that is
> > > > > > reserved in the original (i.e. to be crashed) kernel. The reserved
> > > > > > region is large enough for the crashdump kernel to run completely inside
> > > > > > it and don't modify anything outside it, just read and dump the remains
> > > > > > of the crashed kernel. Using the information from DTB makes the
> > > > > > decompressor place the kernel outside of the dedicated region.
> > > > > >
> > > > > > The log below shows that a zImage and DTB are loaded at 0x18eb8000 and
> > > > > > 0x193f6000 (physical). The kernel is expected to run at 0x18008000, but
> > > > > > it is decompressed to 0x00008000 (see r4 reported before jumping from
> > > > > > within __enter_kernel). If I were to suggest something, there need to be
> > > > > > one more bit of information passed in the DTB telling the decompressor
> > > > > > to use the old masking technique to determain kernel address. It would
> > > > > > be set in the DTB loaded along with the crashdump kernel.
> > > > >
> > > > > Shouldn't the DTB passed to the crashkernel describe which region of
> > > > > memory is to be used instead?
> > > >
> > > > Definitely not. The crashkernel needs to know where the RAM in the
> > > > machine is, so that it can create a coredump of the crashed kernel.
> > >
> > > So the DTB should describe both ;-)
> > >
> > > > > Describing "to use the old masking technique" sounds a bit hackish to me.
> > > > > I guess it cannot just restrict the /memory node to the reserved region,
> > > > > as the crashkernel needs to be able to dump the remains of the crashed
> > > > > kernel, which lie outside this region.
> > > >
> > > > Correct.
> > > >
> > > > > However, something under /chosen should work.
> > > >
> > > > Yet another sticky plaster...
> > >
> > > IMHO the old masking technique is the hacky solution covered by
> > > plasters.
> > >
> >
> > I think debating which solution is the hacky one will not get us anywhere.
> >
> > The simple reality is that the existing solution works fine for
> > existing platforms, and so any changes in the logic will have to be
> > opt-in in one way or the other.
> >
> > Since U-boot supports EFI boot these days, one potential option is to
> > rely on that. I have some changes implementing this that go on top of
> > this patch, but they don't actually rely on it - it was just to
> > prevent lexical conflicts.
> >
> > The only remaining options imo are a kernel command line option, or a
> > DT property that tells the decompressor to look at the memory nodes.
> > But using the DT memory nodes on all platforms like this patch does is
> > obviously just too risky.
> >
> > On another note, I do think the usable-memory-region property should
> > be implemented for ARM as well - relying on this rounding to ensure
> > that the decompressor does the right thing is too fragile.
>
> What is "too fragile" is trying to change this and expecting everything
> to continue working as it did before.
>
That is my point.
> How will switching to EFI help? Won't the crashdump kernel detect EFI
> and try to get the memory map from EFI, whereby it runs into exactly
> the same issue that the DT approach does?
>
No. If you boot from kexec, then the EFI stub is completely
circumvented, and things work as before.
> The current crashkernel situation works precisely because of the 128M
> masking that is being done.
>
Indeed. That is precisely my point.
^ permalink raw reply
* Re: [PATCH] ASoC: fsi: Add missing properties to DT bindings
From: Mark Brown @ 2020-05-19 14:36 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Kuninori Morimoto, Liam Girdwood, Rob Herring, Linux-Renesas,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
ALSA Development Mailing List
In-Reply-To: <CAMuHMdUHHzBNFHy07hgdryJEeHUDMOCP8XauCn7TmUK7uGZcFg@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 492 bytes --]
On Tue, May 19, 2020 at 03:33:01PM +0200, Geert Uytterhoeven wrote:
> On Tue, May 19, 2020 at 2:47 PM Mark Brown <broonie@kernel.org> wrote:
> > This doesn't apply against current code, please check and resend.
> It indeed doesn't apply to your sound/for-next branch.
> It does apply to robh/for-next, which has commit 9f60a65bc5e6cd88
> ("dt-bindings: Clean-up schema indentation formatting"), so I guess
> Rob will have to take it.
Makes sense
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Introduce SMP support for CI20 (based on JZ4780) v8.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
Introduce SMP support for MIPS Creator CI20, which is
based on Ingenic JZ4780 SoC.
^ permalink raw reply
* [PATCH v8 5/6] MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Add 'cpus' node to the jz4740.dtsi, jz4770.dtsi, jz4780.dtsi
and x1000.dtsi files.
Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---
Notes:
v1->v2:
No change.
v2->v3:
No change.
v3->v4:
Rebase on top of kernel 5.6-rc1.
v4->v5:
No change.
v5->v6:
No change.
v6->v7:
Update compatible strings.
v7->v8:
No change.
arch/mips/boot/dts/ingenic/jz4740.dtsi | 14 ++++++++++++++
arch/mips/boot/dts/ingenic/jz4770.dtsi | 15 ++++++++++++++-
arch/mips/boot/dts/ingenic/jz4780.dtsi | 23 +++++++++++++++++++++++
arch/mips/boot/dts/ingenic/x1000.dtsi | 14 ++++++++++++++
4 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/arch/mips/boot/dts/ingenic/jz4740.dtsi b/arch/mips/boot/dts/ingenic/jz4740.dtsi
index a3301ba..1f2f896 100644
--- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
@@ -7,6 +7,20 @@
#size-cells = <1>;
compatible = "ingenic,jz4740";
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-mxu1.0";
+ reg = <0>;
+
+ clocks = <&cgu JZ4740_CLK_CCLK>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
diff --git a/arch/mips/boot/dts/ingenic/jz4770.dtsi b/arch/mips/boot/dts/ingenic/jz4770.dtsi
index 0bfb9ed..12c7101 100644
--- a/arch/mips/boot/dts/ingenic/jz4770.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4770.dtsi
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-
#include <dt-bindings/clock/jz4770-cgu.h>
/ {
@@ -7,6 +6,20 @@
#size-cells = <1>;
compatible = "ingenic,jz4770";
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+ reg = <0>;
+
+ clocks = <&cgu JZ4770_CLK_CCLK>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi
index bb89653..03aeeff 100644
--- a/arch/mips/boot/dts/ingenic/jz4780.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi
@@ -8,6 +8,29 @@
#size-cells = <1>;
compatible = "ingenic,jz4780";
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+ reg = <0>;
+
+ clocks = <&cgu JZ4780_CLK_CPU>;
+ clock-names = "cpu";
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+ reg = <1>;
+
+ clocks = <&cgu JZ4780_CLK_CORE1>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
diff --git a/arch/mips/boot/dts/ingenic/x1000.dtsi b/arch/mips/boot/dts/ingenic/x1000.dtsi
index 147f7d5..2205e1b 100644
--- a/arch/mips/boot/dts/ingenic/x1000.dtsi
+++ b/arch/mips/boot/dts/ingenic/x1000.dtsi
@@ -8,6 +8,20 @@
#size-cells = <1>;
compatible = "ingenic,x1000", "ingenic,x1000e";
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+ reg = <0>;
+
+ clocks = <&cgu X1000_CLK_CPU>;
+ clock-names = "cpu";
+ };
+ };
+
cpuintc: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
--
2.7.4
^ permalink raw reply related
* [PATCH v8 6/6] MIPS: CI20: Update defconfig to support SMP.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Add "CONFIG_SMP=y" and "CONFIG_NR_CPUS=2" to support SMP.
Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---
Notes:
v1->v2:
No change.
v2->v3:
No change.
v3->v4:
Rebase on top of kernel 5.6-rc1.
v4->v5:
No change.
v5->v6:
No change.
v6->v7:
No change.
v7->v8:
No change.
arch/mips/configs/ci20_defconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig
index 0db0088..c8dd136 100644
--- a/arch/mips/configs/ci20_defconfig
+++ b/arch/mips/configs/ci20_defconfig
@@ -1,3 +1,5 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_MODULES=y
CONFIG_KERNEL_XZ=y
--
2.7.4
^ permalink raw reply related
* [PATCH v8 2/6] MIPS: CI20: Modify DTS to support high resolution timer for SMP.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Modify DTS, change tcu channel from 2 to 3, channel #0 and #1 for
per core local timer, #2 for clocksource.
Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---
Notes:
v1->v2:
No change.
v2->v3:
No change.
v3->v4:
Rebase on top of kernel 5.6-rc1.
v4->v5:
Move [5/6] in v4 to this patch, to ensure that we can
git-bisect without ending up with a broken kernel.
v5->v6:
No change.
v6->v7:
Remove unnecessary "ingenic,pwm-channels-mask".
v7->v8:
No change.
arch/mips/boot/dts/ingenic/ci20.dts | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts
index db0ca25..06e3186 100644
--- a/arch/mips/boot/dts/ingenic/ci20.dts
+++ b/arch/mips/boot/dts/ingenic/ci20.dts
@@ -486,7 +486,12 @@
};
&tcu {
- /* 3 MHz for the system timer and clocksource */
- assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>;
- assigned-clock-rates = <3000000>, <3000000>;
+ /*
+ * 750 kHz for the system timers and clocksource,
+ * use channel #0 and #1 for the per cpu system timers,
+ * and use channel #2 for the clocksource.
+ */
+ assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>,
+ <&tcu TCU_CLK_TIMER2>;
+ assigned-clock-rates = <750000>, <750000>, <750000>;
};
--
2.7.4
^ permalink raw reply related
* [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support for SMP.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Enable clock event handling on per CPU core basis.
Make sure that interrupts raised on the first core execute
event handlers on the correct CPU core.
Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---
Notes:
v1->v2:
1.Adjust function naming to make it more reasonable.
2.Replace function smp_call_function_single() with
smp_call_function_single_async() in order to resolve
the warning below:
[ 0.350942] smp: Brought up 1 node, 2 CPUs
[ 0.365497] ------------[ cut here ]------------
[ 0.365522] WARNING: CPU: 0 PID: 1 at kernel/smp.c:300 smp_call_function_single+0x110/0x200
[ 0.365533] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.5.0-rc1+ #5
[ 0.365537] Stack : 00000000 59c73bcd 00000037 80074e80 80000000 80670000 805a0000 80620590
[ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000 80670000 00000001 8fc0dc20 59c73bcd
[ 0.365574] 00000000 00000000 806f0000 80670000 00000000 806dab00 00000000 2d302e35
[ 0.365591] 203a6d6d 806e0000 806e0000 70617773 80670000 00000000 00000000 00000009
[ 0.365610] 00000000 8fc94e20 8fc0de30 80690000 00000018 803592dc 00000000 806d0000
[ 0.365627] ...
[ 0.365634] Call Trace:
[ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
[ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
[ 0.365673] [<8003044c>] __warn+0xc4/0xe8
[ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
[ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
[ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
[ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000 80670000 00000001 8fc0dc20 59c73bcd
[ 0.365574] 00000000 00000000 806f0000 80670000 00000000 806dab00 00000000 2d302e35
[ 0.365591] 203a6d6d 806e0000 806e0000 70617773 80670000 00000000 00000000 00000009
[ 0.365610] 00000000 8fc94e20 8fc0de30 80690000 00000018 803592dc 00000000 806d0000
[ 0.365627] ...
[ 0.365634] Call Trace:
[ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
[ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
[ 0.365673] [<8003044c>] __warn+0xc4/0xe8
[ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
[ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
[ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
v2->v3:
No Change.
v3->v4:
Rebase on top of kernel 5.6-rc1.
v4->v5:
Move the check for (evt->event_handler) from "ingenic_per_cpu_event_handler"
to "ingenic_tcu_cevt_cb".
v5->v6:
No change.
v6->v7:
Remove unnecessary check for "NR_CPUS > 1".
v7->v8:
Use "num_possible_cpus()" instead "NR_CPUS".
Reported-by: kbuild test robot <lkp@intel.com>
drivers/clocksource/ingenic-timer.c | 103 ++++++++++++++++++++++++++++--------
1 file changed, 82 insertions(+), 21 deletions(-)
diff --git a/drivers/clocksource/ingenic-timer.c b/drivers/clocksource/ingenic-timer.c
index 4963336..230e996 100644
--- a/drivers/clocksource/ingenic-timer.c
+++ b/drivers/clocksource/ingenic-timer.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * JZ47xx SoCs TCU IRQ driver
+ * XBurst SoCs TCU IRQ driver
* Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
*/
#include <linux/bitops.h>
@@ -21,18 +22,23 @@
#include <dt-bindings/clock/ingenic,tcu.h>
+static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
+
struct ingenic_soc_info {
unsigned int num_channels;
};
struct ingenic_tcu {
struct regmap *map;
+ struct device_node *np;
struct clk *timer_clk, *cs_clk;
+ unsigned int timer_local[NR_CPUS];
unsigned int timer_channel, cs_channel;
struct clock_event_device cevt;
struct clocksource cs;
- char name[4];
+ char name[8];
unsigned long pwm_channels_mask;
+ int cpu;
};
static struct ingenic_tcu *ingenic_tcu;
@@ -81,6 +87,24 @@ static int ingenic_tcu_cevt_set_next(unsigned long next,
return 0;
}
+static void ingenic_per_cpu_event_handler(void *info)
+{
+ struct clock_event_device *cevt = (struct clock_event_device *) info;
+
+ cevt->event_handler(cevt);
+}
+
+static void ingenic_tcu_per_cpu_cb(struct clock_event_device *evt)
+{
+ struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+ call_single_data_t *csd;
+
+ csd = &per_cpu(ingenic_cevt_csd, tcu->cpu);
+ csd->info = (void *) evt;
+ csd->func = ingenic_per_cpu_event_handler;
+ smp_call_function_single_async(tcu->cpu, csd);
+}
+
static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
@@ -89,7 +113,7 @@ static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
if (evt->event_handler)
- evt->event_handler(evt);
+ ingenic_tcu_per_cpu_cb(evt);
return IRQ_HANDLED;
}
@@ -105,14 +129,21 @@ static struct clk * __init ingenic_tcu_get_clock(struct device_node *np, int id)
return of_clk_get_from_provider(&args);
}
-static int __init ingenic_tcu_timer_init(struct device_node *np,
- struct ingenic_tcu *tcu)
+static int ingenic_tcu_setup_per_cpu_cevt(struct device_node *np,
+ unsigned int channel)
{
- unsigned int timer_virq, channel = tcu->timer_channel;
+ unsigned int timer_virq;
struct irq_domain *domain;
+ struct ingenic_tcu *tcu;
unsigned long rate;
int err;
+ tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
+ if (!tcu)
+ return -ENOMEM;
+
+ tcu->map = ingenic_tcu->map;
+
tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
if (IS_ERR(tcu->timer_clk))
return PTR_ERR(tcu->timer_clk);
@@ -139,13 +170,15 @@ static int __init ingenic_tcu_timer_init(struct device_node *np,
goto err_clk_disable;
}
- snprintf(tcu->name, sizeof(tcu->name), "TCU");
+ snprintf(tcu->name, sizeof(tcu->name), "TCU%u", channel);
err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
tcu->name, &tcu->cevt);
if (err)
goto err_irq_dispose_mapping;
+ tcu->cpu = smp_processor_id();
+ tcu->timer_channel = channel;
tcu->cevt.cpumask = cpumask_of(smp_processor_id());
tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
tcu->cevt.name = tcu->name;
@@ -166,6 +199,25 @@ static int __init ingenic_tcu_timer_init(struct device_node *np,
return err;
}
+static int ingenic_tcu_setup_cevt(unsigned int cpu)
+{
+ int ret;
+
+ ret = ingenic_tcu_setup_per_cpu_cevt(ingenic_tcu->np,
+ ingenic_tcu->timer_local[cpu]);
+ if (ret)
+ goto err_tcu_clocksource_cleanup;
+
+ return 0;
+
+err_tcu_clocksource_cleanup:
+ clocksource_unregister(&ingenic_tcu->cs);
+ clk_disable_unprepare(ingenic_tcu->cs_clk);
+ clk_put(ingenic_tcu->cs_clk);
+ kfree(ingenic_tcu);
+ return ret;
+}
+
static int __init ingenic_tcu_clocksource_init(struct device_node *np,
struct ingenic_tcu *tcu)
{
@@ -240,6 +292,7 @@ static int __init ingenic_tcu_init(struct device_node *np)
const struct ingenic_soc_info *soc_info = id->data;
struct ingenic_tcu *tcu;
struct regmap *map;
+ unsigned cpu = 0;
long rate;
int ret;
@@ -253,13 +306,18 @@ static int __init ingenic_tcu_init(struct device_node *np)
if (!tcu)
return -ENOMEM;
- /* Enable all TCU channels for PWM use by default except channels 0/1 */
- tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2);
+ /*
+ * Enable all TCU channels for PWM use by default except channels 0/1,
+ * and channel 2 if target CPU is JZ4780 and SMP is selected.
+ */
+ tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
+ num_possible_cpus() + 1);
of_property_read_u32(np, "ingenic,pwm-channels-mask",
(u32 *)&tcu->pwm_channels_mask);
- /* Verify that we have at least two free channels */
- if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels - 2) {
+ /* Verify that we have at least num_possible_cpus() + 1 free channels */
+ if (hweight8(tcu->pwm_channels_mask) >
+ soc_info->num_channels - num_possible_cpus() + 1) {
pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
tcu->pwm_channels_mask);
ret = -EINVAL;
@@ -267,13 +325,19 @@ static int __init ingenic_tcu_init(struct device_node *np)
}
tcu->map = map;
+ tcu->np = np;
ingenic_tcu = tcu;
- tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask,
+ tcu->timer_local[cpu] = find_first_zero_bit(&tcu->pwm_channels_mask,
soc_info->num_channels);
+
+ for (cpu = 1; cpu < num_possible_cpus(); cpu++)
+ tcu->timer_local[cpu] = find_next_zero_bit(
+ &tcu->pwm_channels_mask, soc_info->num_channels,
+ tcu->timer_local[cpu - 1] + 1);
+
tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
- soc_info->num_channels,
- tcu->timer_channel + 1);
+ soc_info->num_channels, tcu->timer_local[cpu - 1] + 1);
ret = ingenic_tcu_clocksource_init(np, tcu);
if (ret) {
@@ -281,9 +345,10 @@ static int __init ingenic_tcu_init(struct device_node *np)
goto err_free_ingenic_tcu;
}
- ret = ingenic_tcu_timer_init(np, tcu);
- if (ret)
- goto err_tcu_clocksource_cleanup;
+ /* Setup clock events on each CPU core */
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst: online",
+ ingenic_tcu_setup_cevt, NULL);
+ WARN_ON(ret < 0);
/* Register the sched_clock at the end as there's no way to undo it */
rate = clk_get_rate(tcu->cs_clk);
@@ -291,10 +356,6 @@ static int __init ingenic_tcu_init(struct device_node *np)
return 0;
-err_tcu_clocksource_cleanup:
- clocksource_unregister(&tcu->cs);
- clk_disable_unprepare(tcu->cs_clk);
- clk_put(tcu->cs_clk);
err_free_ingenic_tcu:
kfree(tcu);
return ret;
--
2.7.4
^ permalink raw reply related
* [PATCH v8 0/6] Introduce SMP support for CI20 (based on JZ4780).
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Introduce SMP support for MIPS Creator CI20, which is
based on Ingenic JZ4780 SoC.
周琰杰 (Zhou Yanjie) (6):
MIPS: JZ4780: Introduce SMP support.
MIPS: CI20: Modify DTS to support high resolution timer for SMP.
clocksource: Ingenic: Add high resolution timer support for SMP.
dt-bindings: MIPS: Document Ingenic SoCs binding.
MIPS: Ingenic: Add 'cpus' node for Ingenic SoCs.
MIPS: CI20: Update defconfig to support SMP.
.../bindings/mips/ingenic/ingenic,cpu.yaml | 57 +++++
arch/mips/boot/dts/ingenic/ci20.dts | 11 +-
arch/mips/boot/dts/ingenic/jz4740.dtsi | 14 ++
arch/mips/boot/dts/ingenic/jz4770.dtsi | 15 +-
arch/mips/boot/dts/ingenic/jz4780.dtsi | 23 ++
arch/mips/boot/dts/ingenic/x1000.dtsi | 14 ++
arch/mips/configs/ci20_defconfig | 2 +
arch/mips/include/asm/mach-jz4740/smp.h | 87 +++++++
arch/mips/jz4740/Kconfig | 2 +
arch/mips/jz4740/Makefile | 5 +
arch/mips/jz4740/prom.c | 4 +
arch/mips/jz4740/smp-entry.S | 57 +++++
arch/mips/jz4740/smp.c | 258 +++++++++++++++++++++
arch/mips/kernel/idle.c | 35 ++-
drivers/clocksource/ingenic-timer.c | 103 ++++++--
15 files changed, 661 insertions(+), 26 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
create mode 100644 arch/mips/jz4740/smp-entry.S
create mode 100644 arch/mips/jz4740/smp.c
--
2.7.4
^ permalink raw reply
* [PATCH v8 4/6] dt-bindings: MIPS: Document Ingenic SoCs binding.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Document the available properties for the SoC root node and the
CPU nodes of the devicetree for the Ingenic XBurst SoCs.
Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
---
Notes:
v1->v2:
Change the two Document from txt to yaml.
v2->v3:
Fix formatting errors.
v3->v4:
Fix bugs in the two yaml files.
v4->v5:
No change.
v5->v6:
Rewrite the two yaml files.
v6->v7:
1.Update compatible strings in "ingenic,cpu.yaml".
2.Fix formatting errors, and enum for compatible strings.
3.Remove unnecessary "ingenic,soc.yaml".
v7->v8:
No change.
.../bindings/mips/ingenic/ingenic,cpu.yaml | 57 ++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
diff --git a/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
new file mode 100644
index 00000000..afb0207
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/ingenic/ingenic,cpu.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mips/ingenic/ingenic,cpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bindings for Ingenic XBurst family CPUs
+
+maintainers:
+ - 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+
+description:
+ Ingenic XBurst family CPUs shall have the following properties.
+
+properties:
+ compatible:
+ oneOf:
+
+ - description: Ingenic XBurst®1 CPU Cores
+ items:
+ enum:
+ - ingenic,xburst-mxu1.0
+ - ingenic,xburst-fpu1.0-mxu1.1
+ - ingenic,xburst-fpu2.0-mxu2.0
+
+ - description: Ingenic XBurst®2 CPU Cores
+ items:
+ enum:
+ - ingenic,xburst2-fpu2.1-mxu2.1-smt
+
+ reg:
+ maxItems: 1
+
+required:
+ - device_type
+ - compatible
+ - reg
+
+examples:
+ - |
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+ reg = <0>;
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ compatible = "ingenic,xburst-fpu1.0-mxu1.1";
+ reg = <1>;
+ };
+ };
+...
--
2.7.4
^ permalink raw reply related
* [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
From: 周琰杰 (Zhou Yanjie) @ 2020-05-19 14:35 UTC (permalink / raw)
To: linux-mips
Cc: linux-kernel, devicetree, tsbogend, paulburton, jiaxun.yang,
chenhc, tglx, robh+dt, daniel.lezcano, keescook, paul, krzk, hns,
ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu, sernia.zhou,
zhenwenjin
In-Reply-To: <1589898923-60048-1-git-send-email-zhouyanjie@wanyeetech.com>
Forward port smp support from kernel 3.18.3 of CI20_linux
to upstream kernel 5.6.
Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
Tested-by: Paul Boddie <paul@boddie.org.uk>
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
Notes:
v1->v2:
1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
2.Add a timeout check for "jz4780_boot_secondary()" to avoid a dead loop.
3.Replace hard code in smp.c with macro.
v2->v3:
1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in smp.c.
2.Use "for_each_of_cpu_node" instead "for_each_compatible_node" in smp.c.
3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" in smp.c.
4.Move LCR related operations to jz4780-cgu.c.
v3->v4:
Rebase on top of kernel 5.6-rc1.
v4->v5:
1.Splitting changes involving "jz4780-cgu.c" into separate commit.
2.Use "request_irq()" replace "setup_irq()".
v5->v6:
In order to have a kernel that works on multiple SoCs at the same
time, use "IS_ENABLED()" replace "#ifdef".
v6->v7:
1.SMP has be decoupled from the SoC version.
2.Add mailboxes 3 and 4 for XBurst.
3.Adjust code in "jz4780_smp_prepare_cpus()".
4."jz4780_smp_init()" has be marked "__init".
v7->v8:
No change.
arch/mips/include/asm/mach-jz4740/smp.h | 87 +++++++++++
arch/mips/jz4740/Kconfig | 2 +
arch/mips/jz4740/Makefile | 5 +
arch/mips/jz4740/prom.c | 4 +
arch/mips/jz4740/smp-entry.S | 57 +++++++
arch/mips/jz4740/smp.c | 258 ++++++++++++++++++++++++++++++++
arch/mips/kernel/idle.c | 35 ++++-
7 files changed, 447 insertions(+), 1 deletion(-)
create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
create mode 100644 arch/mips/jz4740/smp-entry.S
create mode 100644 arch/mips/jz4740/smp.c
diff --git a/arch/mips/include/asm/mach-jz4740/smp.h b/arch/mips/include/asm/mach-jz4740/smp.h
new file mode 100644
index 00000000..86f660f
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/smp.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
+ * JZ4780 SMP definitions
+ */
+
+#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
+#define __MIPS_ASM_MACH_JZ4740_SMP_H__
+
+#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
+#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2, val)
+
+#define read_c0_corestatus() __read_32bit_c0_register($12, 3)
+#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3, val)
+
+#define read_c0_reim() __read_32bit_c0_register($12, 4)
+#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
+
+#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
+#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0, val)
+
+#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
+#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1, val)
+
+#define read_c0_mailbox2() __read_32bit_c0_register($20, 2)
+#define write_c0_mailbox2(val) __write_32bit_c0_register($20, 2, val)
+
+#define read_c0_mailbox3() __read_32bit_c0_register($20, 3)
+#define write_c0_mailbox3(val) __write_32bit_c0_register($20, 3, val)
+
+#define smp_clr_pending(mask) do { \
+ unsigned int stat; \
+ stat = read_c0_corestatus(); \
+ stat &= ~((mask) & 0xff); \
+ write_c0_corestatus(stat); \
+ } while (0)
+
+/*
+ * Core Control register
+ */
+#define CORECTRL_SLEEP1M_SHIFT 17
+#define CORECTRL_SLEEP1M (_ULCAST_(0x1) << CORECTRL_SLEEP1M_SHIFT)
+#define CORECTRL_SLEEP0M_SHIFT 16
+#define CORECTRL_SLEEP0M (_ULCAST_(0x1) << CORECTRL_SLEEP0M_SHIFT)
+#define CORECTRL_RPC1_SHIFT 9
+#define CORECTRL_RPC1 (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
+#define CORECTRL_RPC0_SHIFT 8
+#define CORECTRL_RPC0 (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
+#define CORECTRL_SWRST1_SHIFT 1
+#define CORECTRL_SWRST1 (_ULCAST_(0x1) << CORECTRL_SWRST1_SHIFT)
+#define CORECTRL_SWRST0_SHIFT 0
+#define CORECTRL_SWRST0 (_ULCAST_(0x1) << CORECTRL_SWRST0_SHIFT)
+
+/*
+ * Core Status register
+ */
+#define CORESTATUS_SLEEP1_SHIFT 17
+#define CORESTATUS_SLEEP1 (_ULCAST_(0x1) << CORESTATUS_SLEEP1_SHIFT)
+#define CORESTATUS_SLEEP0_SHIFT 16
+#define CORESTATUS_SLEEP0 (_ULCAST_(0x1) << CORESTATUS_SLEEP0_SHIFT)
+#define CORESTATUS_IRQ1P_SHIFT 9
+#define CORESTATUS_IRQ1P (_ULCAST_(0x1) << CORESTATUS_IRQ1P_SHIFT)
+#define CORESTATUS_IRQ0P_SHIFT 8
+#define CORESTATUS_IRQ0P (_ULCAST_(0x1) << CORESTATUS_IRQ8P_SHIFT)
+#define CORESTATUS_MIRQ1P_SHIFT 1
+#define CORESTATUS_MIRQ1P (_ULCAST_(0x1) << CORESTATUS_MIRQ1P_SHIFT)
+#define CORESTATUS_MIRQ0P_SHIFT 0
+#define CORESTATUS_MIRQ0P (_ULCAST_(0x1) << CORESTATUS_MIRQ0P_SHIFT)
+
+/*
+ * Reset Entry & IRQ Mask register
+ */
+#define REIM_ENTRY_SHIFT 16
+#define REIM_ENTRY (_ULCAST_(0xffff) << REIM_ENTRY_SHIFT)
+#define REIM_IRQ1M_SHIFT 9
+#define REIM_IRQ1M (_ULCAST_(0x1) << REIM_IRQ1M_SHIFT)
+#define REIM_IRQ0M_SHIFT 8
+#define REIM_IRQ0M (_ULCAST_(0x1) << REIM_IRQ0M_SHIFT)
+#define REIM_MBOXIRQ1M_SHIFT 1
+#define REIM_MBOXIRQ1M (_ULCAST_(0x1) << REIM_MBOXIRQ1M_SHIFT)
+#define REIM_MBOXIRQ0M_SHIFT 0
+#define REIM_MBOXIRQ0M (_ULCAST_(0x1) << REIM_MBOXIRQ0M_SHIFT)
+
+extern void jz4780_smp_init(void);
+extern void jz4780_secondary_cpu_entry(void);
+
+#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
index 412d2fa..2b88557 100644
--- a/arch/mips/jz4740/Kconfig
+++ b/arch/mips/jz4740/Kconfig
@@ -34,9 +34,11 @@ config MACH_JZ4770
config MACH_JZ4780
bool
+ select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select MIPS_CPU_SCACHE
select SYS_HAS_CPU_MIPS32_R2
select SYS_SUPPORTS_HIGHMEM
+ select SYS_SUPPORTS_SMP
config MACH_X1000
bool
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
index 6de14c0..0a0f024 100644
--- a/arch/mips/jz4740/Makefile
+++ b/arch/mips/jz4740/Makefile
@@ -12,3 +12,8 @@ CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
# PM support
obj-$(CONFIG_PM) += pm.o
+
+# SMP support
+
+obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_SMP) += smp-entry.o
diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c
index ff4555c..4acf5c2c 100644
--- a/arch/mips/jz4740/prom.c
+++ b/arch/mips/jz4740/prom.c
@@ -8,10 +8,14 @@
#include <asm/bootinfo.h>
#include <asm/fw/fw.h>
+#include <asm/mach-jz4740/smp.h>
void __init prom_init(void)
{
fw_init_cmdline();
+
+ if (IS_ENABLED(CONFIG_SMP))
+ jz4780_smp_init();
}
void __init prom_free_prom_memory(void)
diff --git a/arch/mips/jz4740/smp-entry.S b/arch/mips/jz4740/smp-entry.S
new file mode 100644
index 00000000..20049a3
--- /dev/null
+++ b/arch/mips/jz4740/smp-entry.S
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
+ * JZ4780 SMP entry point
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+
+#define CACHE_SIZE (32 * 1024)
+#define CACHE_LINESIZE 32
+
+.extern jz4780_cpu_entry_sp
+.extern jz4780_cpu_entry_gp
+
+.section .text.smp-entry
+.balign 0x10000
+.set noreorder
+LEAF(jz4780_secondary_cpu_entry)
+ mtc0 zero, CP0_CAUSE
+
+ li t0, ST0_CU0
+ mtc0 t0, CP0_STATUS
+
+ /* cache setup */
+ li t0, KSEG0
+ ori t1, t0, CACHE_SIZE
+ mtc0 zero, CP0_TAGLO, 0
+1: cache Index_Store_Tag_I, 0(t0)
+ cache Index_Store_Tag_D, 0(t0)
+ bne t0, t1, 1b
+ addiu t0, t0, CACHE_LINESIZE
+
+ /* kseg0 cache attribute */
+ mfc0 t0, CP0_CONFIG, 0
+ ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT
+ mtc0 t0, CP0_CONFIG, 0
+
+ /* pagemask */
+ mtc0 zero, CP0_PAGEMASK, 0
+
+ /* retrieve sp */
+ la t0, jz4780_cpu_entry_sp
+ lw sp, 0(t0)
+
+ /* retrieve gp */
+ la t0, jz4780_cpu_entry_gp
+ lw gp, 0(t0)
+
+ /* jump to the kernel in kseg0 */
+ la t0, smp_bootstrap
+ jr t0
+ nop
+ END(jz4780_secondary_cpu_entry)
diff --git a/arch/mips/jz4740/smp.c b/arch/mips/jz4740/smp.c
new file mode 100644
index 00000000..d95d22a
--- /dev/null
+++ b/arch/mips/jz4740/smp.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
+ * JZ4780 SMP
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+#include <linux/smp.h>
+#include <linux/tick.h>
+#include <asm/mach-jz4740/smp.h>
+#include <asm/smp-ops.h>
+
+static struct clk *cpu_clock_gates[CONFIG_NR_CPUS] = { NULL };
+
+u32 jz4780_cpu_entry_sp;
+u32 jz4780_cpu_entry_gp;
+
+static struct cpumask cpu_running;
+
+static DEFINE_SPINLOCK(smp_lock);
+
+static irqreturn_t mbox_handler(int irq, void *dev_id)
+{
+ int cpu = smp_processor_id();
+ u32 action, status;
+
+ spin_lock(&smp_lock);
+
+ switch (cpu) {
+ case 0:
+ action = read_c0_mailbox0();
+ write_c0_mailbox0(0);
+ break;
+ case 1:
+ action = read_c0_mailbox1();
+ write_c0_mailbox1(0);
+ break;
+ case 2:
+ action = read_c0_mailbox2();
+ write_c0_mailbox2(0);
+ break;
+ case 3:
+ action = read_c0_mailbox3();
+ write_c0_mailbox3(0);
+ break;
+ default:
+ panic("unhandled cpu %d!", cpu);
+ }
+
+ /* clear pending mailbox interrupt */
+ status = read_c0_corestatus();
+ status &= ~(CORESTATUS_MIRQ0P << cpu);
+ write_c0_corestatus(status);
+
+ spin_unlock(&smp_lock);
+
+ if (action & SMP_RESCHEDULE_YOURSELF)
+ scheduler_ipi();
+ if (action & SMP_CALL_FUNCTION)
+ generic_smp_call_function_interrupt();
+
+ return IRQ_HANDLED;
+}
+
+static void jz4780_smp_setup(void)
+{
+ u32 addr, reim;
+ int cpu;
+
+ reim = read_c0_reim();
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+ __cpu_number_map[cpu] = cpu;
+ __cpu_logical_map[cpu] = cpu;
+ set_cpu_possible(cpu, true);
+ }
+
+ /* mask mailbox interrupts for this core */
+ reim &= ~REIM_MBOXIRQ0M;
+ write_c0_reim(reim);
+
+ /* clear mailboxes & pending mailbox IRQs */
+ write_c0_mailbox0(0);
+ write_c0_mailbox1(0);
+ write_c0_corestatus(0);
+
+ /* set reset entry point */
+ addr = KSEG1ADDR((u32)&jz4780_secondary_cpu_entry);
+ WARN_ON(addr & ~REIM_ENTRY);
+ reim &= ~REIM_ENTRY;
+ reim |= addr & REIM_ENTRY;
+
+ /* unmask mailbox interrupts for this core */
+ reim |= REIM_MBOXIRQ0M;
+ write_c0_reim(reim);
+ set_c0_status(STATUSF_IP3);
+ irq_enable_hazard();
+
+ cpumask_set_cpu(cpu, &cpu_running);
+}
+
+static void jz4780_smp_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *cpu_node;
+ unsigned cpu, ctrl;
+ int err;
+
+ /* setup the mailbox IRQ */
+ err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
+ IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
+ if (err)
+ pr_err("request_irq() on core mailbox failed\n");
+
+ ctrl = read_c0_corectrl();
+
+ for_each_of_cpu_node(cpu_node) {
+ cpu = of_cpu_node_to_id(cpu_node);
+ if (cpu < 0) {
+ pr_err("Failed to read index of %s\n",
+ cpu_node->full_name);
+ continue;
+ }
+
+ /* use reset entry point from REIM register */
+ ctrl |= CORECTRL_RPC0 << cpu;
+
+ cpu_clock_gates[cpu] = of_clk_get(cpu_node, 0);
+ if (IS_ERR(cpu_clock_gates[cpu])) {
+ cpu_clock_gates[cpu] = NULL;
+ continue;
+ }
+
+ err = clk_prepare(cpu_clock_gates[cpu]);
+ if (err)
+ pr_err("Failed to prepare CPU clock gate\n");
+ }
+
+ write_c0_corectrl(ctrl);
+}
+
+static int jz4780_boot_secondary(int cpu, struct task_struct *idle)
+{
+ unsigned long flags;
+ u32 ctrl;
+
+ spin_lock_irqsave(&smp_lock, flags);
+
+ /* ensure the core is in reset */
+ ctrl = read_c0_corectrl();
+ ctrl |= CORECTRL_SWRST0 << cpu;
+ write_c0_corectrl(ctrl);
+
+ /* ungate core clock */
+ if (cpu_clock_gates[cpu])
+ clk_enable(cpu_clock_gates[cpu]);
+
+ /* set entry sp/gp register values */
+ jz4780_cpu_entry_sp = __KSTK_TOS(idle);
+ jz4780_cpu_entry_gp = (u32)task_thread_info(idle);
+ smp_wmb();
+
+ /* take the core out of reset */
+ ctrl &= ~(CORECTRL_SWRST0 << cpu);
+ write_c0_corectrl(ctrl);
+
+ cpumask_set_cpu(cpu, &cpu_running);
+
+ spin_unlock_irqrestore(&smp_lock, flags);
+
+ return 0;
+}
+
+static void jz4780_init_secondary(void)
+{
+}
+
+static void jz4780_smp_finish(void)
+{
+ u32 reim;
+
+ spin_lock(&smp_lock);
+
+ /* unmask mailbox interrupts for this core */
+ reim = read_c0_reim();
+ reim |= REIM_MBOXIRQ0M << smp_processor_id();
+ write_c0_reim(reim);
+
+ spin_unlock(&smp_lock);
+
+ /* unmask interrupts for this core */
+ change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 |
+ STATUSF_IP1 | STATUSF_IP0);
+ irq_enable_hazard();
+
+ /* force broadcast timer */
+ tick_broadcast_force();
+}
+
+static void jz4780_send_ipi_single_locked(int cpu, unsigned int action)
+{
+ u32 mbox;
+
+ switch (cpu) {
+ case 0:
+ mbox = read_c0_mailbox0();
+ write_c0_mailbox0(mbox | action);
+ break;
+ case 1:
+ mbox = read_c0_mailbox1();
+ write_c0_mailbox1(mbox | action);
+ break;
+ default:
+ panic("unhandled cpu %d!", cpu);
+ }
+}
+
+static void jz4780_send_ipi_single(int cpu, unsigned int action)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&smp_lock, flags);
+ jz4780_send_ipi_single_locked(cpu, action);
+ spin_unlock_irqrestore(&smp_lock, flags);
+}
+
+static void jz4780_send_ipi_mask(const struct cpumask *mask,
+ unsigned int action)
+{
+ unsigned long flags;
+ int cpu;
+
+ spin_lock_irqsave(&smp_lock, flags);
+
+ for_each_cpu(cpu, mask)
+ jz4780_send_ipi_single_locked(cpu, action);
+
+ spin_unlock_irqrestore(&smp_lock, flags);
+}
+
+static struct plat_smp_ops jz4780_smp_ops = {
+ .send_ipi_single = jz4780_send_ipi_single,
+ .send_ipi_mask = jz4780_send_ipi_mask,
+ .init_secondary = jz4780_init_secondary,
+ .smp_finish = jz4780_smp_finish,
+ .boot_secondary = jz4780_boot_secondary,
+ .smp_setup = jz4780_smp_setup,
+ .prepare_cpus = jz4780_smp_prepare_cpus,
+};
+
+void __init jz4780_smp_init(void)
+{
+ register_smp_ops(&jz4780_smp_ops);
+}
diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
index 37f8e78..d33f2d4 100644
--- a/arch/mips/kernel/idle.c
+++ b/arch/mips/kernel/idle.c
@@ -18,6 +18,7 @@
#include <asm/cpu-type.h>
#include <asm/idle.h>
#include <asm/mipsregs.h>
+#include <asm/r4kcache.h>
/*
* Not all of the MIPS CPUs have the "wait" instruction available. Moreover,
@@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
}
/*
+ * The Ingenic jz4780 SMP variant has to write back dirty cache lines before
+ * executing wait. The CPU & cache clock will be gated until we return from
+ * the wait, and if another core attempts to access data from our data cache
+ * during this time then it will lock up.
+ */
+void jz4780_smp_wait_irqoff(void)
+{
+ unsigned long pending = read_c0_cause() & read_c0_status() & CAUSEF_IP;
+
+ /*
+ * Going to idle has a significant overhead due to the cache flush so
+ * try to avoid it if we'll immediately be woken again due to an IRQ.
+ */
+ if (!need_resched() && !pending) {
+ r4k_blast_dcache();
+
+ __asm__(
+ " .set push \n"
+ " .set mips3 \n"
+ " sync \n"
+ " wait \n"
+ " .set pop \n");
+ }
+
+ local_irq_enable();
+}
+
+/*
* Au1 'wait' is only useful when the 32kHz counter is used as timer,
* since coreclock (and the cp0 counter) stops upon executing it. Only an
* interrupt can wake it, so they must be enabled before entering idle modes.
@@ -172,7 +201,6 @@ void __init check_wait(void)
case CPU_CAVIUM_OCTEON_PLUS:
case CPU_CAVIUM_OCTEON2:
case CPU_CAVIUM_OCTEON3:
- case CPU_XBURST:
case CPU_LOONGSON32:
case CPU_XLR:
case CPU_XLP:
@@ -246,6 +274,11 @@ void __init check_wait(void)
cpu_wait = r4k_wait;
*/
break;
+ case CPU_XBURST:
+ if (IS_ENABLED(CONFIG_SMP))
+ cpu_wait = jz4780_smp_wait_irqoff;
+ else
+ cpu_wait = r4k_wait;
default:
break;
}
--
2.7.4
^ permalink raw reply related
* Re: [PATCH v7 3/3] power: supply: bq25150 introduce the bq25150
From: Andrew F. Davis @ 2020-05-19 14:46 UTC (permalink / raw)
To: Dan Murphy, sre
Cc: sspatil, linux-pm, robh, linux-kernel, devicetree,
Ricardo Rivera-Matos
In-Reply-To: <20200519141916.28282-4-dmurphy@ti.com>
On 5/19/20 10:19 AM, Dan Murphy wrote:
> +static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val)
> +{
> + int i;
> +
> + if (val > BQ2515X_ILIM_MAX || val < BQ2515X_ILIM_MIN)
> + return -EINVAL;
Clamp to these limits, not reject. Or better, modify the below loop so
it clamps to the highest or lowest value in bq2515x_ilim_lvl_values[],
then drop these #defines.
> +
> + for (i = 0; i < ARRAY_SIZE(bq2515x_ilim_lvl_values); i++) {
> + if (val == bq2515x_ilim_lvl_values[i])
> + break;
> +
> + if (val > bq2515x_ilim_lvl_values[i - 1] &&
Index out of bounds for the i = 0 case.
> + val < bq2515x_ilim_lvl_values[i]) {
> + if (val - bq2515x_ilim_lvl_values[i - 1] <
> + bq2515x_ilim_lvl_values[i] - val) {
You are still missing the case where the value is closer to the [i]
element, you check that it is between [i-1] and [i], but only chose
[i-1] when it is closer to that than [i] but equal and greater case is
missing.
Given this sets input current limits, would instead always rounding down
be the safer option?
Andrew
> + i = i - 1;
> + break;
> + }
> + }
> + }
> +
> + return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i);
> +}
^ permalink raw reply
* Re: [PATCH v9 3/3] phy: intel: Add driver support for ComboPhy
From: Kishon Vijay Abraham I @ 2020-05-19 14:55 UTC (permalink / raw)
To: Dilip Kota, linux-kernel, vkoul, devicetree
Cc: robh, andriy.shevchenko, cheol.yong.kim, chuanhua.lei, qi-ming.wu,
yixin.zhu
In-Reply-To: <7b313826f46b9006a3ba98c0613e8f88f293a074.1589868358.git.eswara.kota@linux.intel.com>
Hi Dilip,
On 5/19/2020 11:49 AM, Dilip Kota wrote:
> ComboPhy subsystem provides PHYs for various
> controllers like PCIe, SATA and EMAC.
>
> Signed-off-by: Dilip Kota <eswara.kota@linux.intel.com>
> ---
> Changes on v9:
> Add Acked-By: Vinod Koul <vkoul@kernel.org>
I added Acked-By myself and merged this patch.
Thanks
Kishon
>
> Fix compiler warning
> drivers/phy/intel/phy-intel-combo.c:229:6: warning: cb_mode may be used
> uninitialized in this function [-Wmaybe-uninitialized]
> ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode);
> ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> drivers/phy/intel/phy-intel-combo.c:204:24: note: cb_mode was declared here
> enum intel_combo_mode cb_mode;
>
> Changes on v8:
> As per PHY Maintainer's request add description for doing register access
> through regmap in comments.
>
> Changes on v7:
> Use device_node_to_regmap instead of fwnode_to_regmap
>
> Changes on v6:
> No changes
>
> Changes on v5:
> Add changes as per inputs from Andy and Rob:
> DT node uses phy-mode values as defined in "include/dt-bindings/phy/phy.h",
> add changes to handle it.
> ComboPhy no longer has children nodes, and children node properties(reset)
> moved to parent node, so do the code changes accordingly.
> Add _xlate() function to pass the appropriate phy handle.
> Fix couple of nitpicks.
>
> Changes on v4:
> Address review comments
> Remove dependency on OF config
> Update copyright to 2019-2020
> Define register macro PAD_DIS_CFG instead of const variable inside function.
> Improve the error prints, and error returns.
> Call put_device(dev), for get_dev_from_fwnode()
> Move platform_set_drvdata() at the end of the probe().
> Correct alignment in phy_ops intel_cbphy_ops.
> Correct commented lines with proper vocabulary and punctuation.
> Add/remove commas for the required constant arrays and enums.
> Remove in driver:
> linux/kernel.h, not required
> macros: PCIE_PHY_MPLLA_CTRL, PCIE_PHY_MPLLB_CTRL
> temp variable u32 prop;
> Change function names:
> intel_cbphy_iphy_dt_parse() -> intel_cbphy_iphy_fwnode_parse()
> intel_cbphy_dt_sanity_check() -> intel_cbphy_sanity_check()
> intel_cbphy_dt_parse() -> intel_cbphy_fwnode_parse()
>
> Changes on v3:
> Remove intel_iphy_names
> Remove struct phy in struct intel_cbphy_iphy
> Imporve if conditions logic
> Use fwnode_to_regmap()
> Call devm_of_platform_populate() to populate child nodes
> Fix reset sequence during phy_init
> Add SoC specific compatible "intel,combophy-lgm"
> Add description for enums
> Remove default case in switch {} intel_cbphy_set_mode() as it
> never happens.
> Use mutex_lock to synchronise combophy initialization across
> two phys.
> Change init_cnt to u32 datatype as it is within mutex lock.
> Correct error handling of
> fwnode_property_read_u32_array(fwnode, "intel,phy-mode", ...)
>
> drivers/phy/intel/Kconfig | 14 +
> drivers/phy/intel/Makefile | 1 +
> drivers/phy/intel/phy-intel-combo.c | 632 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 647 insertions(+)
> create mode 100644 drivers/phy/intel/phy-intel-combo.c
>
> diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig
> index 4ea6a8897cd7..3b40eb7b4fb4 100644
> --- a/drivers/phy/intel/Kconfig
> +++ b/drivers/phy/intel/Kconfig
> @@ -2,6 +2,20 @@
> #
> # Phy drivers for Intel Lightning Mountain(LGM) platform
> #
> +config PHY_INTEL_COMBO
> + bool "Intel ComboPHY driver"
> + depends on X86 || COMPILE_TEST
> + depends on OF && HAS_IOMEM
> + select MFD_SYSCON
> + select GENERIC_PHY
> + select REGMAP
> + help
> + Enable this to support Intel ComboPhy.
> +
> + This driver configures ComboPhy subsystem on Intel gateway
> + chipsets which provides PHYs for various controllers, EMAC,
> + SATA and PCIe.
> +
> config PHY_INTEL_EMMC
> tristate "Intel EMMC PHY driver"
> select GENERIC_PHY
> diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile
> index 6b876a75599d..233d530dadde 100644
> --- a/drivers/phy/intel/Makefile
> +++ b/drivers/phy/intel/Makefile
> @@ -1,2 +1,3 @@
> # SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_PHY_INTEL_COMBO) += phy-intel-combo.o
> obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o
> diff --git a/drivers/phy/intel/phy-intel-combo.c b/drivers/phy/intel/phy-intel-combo.c
> new file mode 100644
> index 000000000000..c2a35be4cdfb
> --- /dev/null
> +++ b/drivers/phy/intel/phy-intel-combo.c
> @@ -0,0 +1,632 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Intel Combo-PHY driver
> + *
> + * Copyright (C) 2019-2020 Intel Corporation.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +
> +#include <dt-bindings/phy/phy.h>
> +
> +#define PCIE_PHY_GEN_CTRL 0x00
> +#define PCIE_PHY_CLK_PAD BIT(17)
> +
> +#define PAD_DIS_CFG 0x174
> +
> +#define PCS_XF_ATE_OVRD_IN_2 0x3008
> +#define ADAPT_REQ_MSK GENMASK(5, 4)
> +
> +#define PCS_XF_RX_ADAPT_ACK 0x3010
> +#define RX_ADAPT_ACK_BIT BIT(0)
> +
> +#define CR_ADDR(addr, lane) (((addr) + (lane) * 0x100) << 2)
> +#define REG_COMBO_MODE(x) ((x) * 0x200)
> +#define REG_CLK_DISABLE(x) ((x) * 0x200 + 0x124)
> +
> +#define COMBO_PHY_ID(x) ((x)->parent->id)
> +#define PHY_ID(x) ((x)->id)
> +
> +#define CLK_100MHZ 100000000
> +#define CLK_156_25MHZ 156250000
> +
> +static const unsigned long intel_iphy_clk_rates[] = {
> + CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ,
> +};
> +
> +enum {
> + PHY_0,
> + PHY_1,
> + PHY_MAX_NUM
> +};
> +
> +/*
> + * Clock Register bit fields to enable clocks
> + * for ComboPhy according to the mode.
> + */
> +enum intel_phy_mode {
> + PHY_PCIE_MODE = 0,
> + PHY_XPCS_MODE,
> + PHY_SATA_MODE,
> +};
> +
> +/* ComboPhy mode Register values */
> +enum intel_combo_mode {
> + PCIE0_PCIE1_MODE = 0,
> + PCIE_DL_MODE,
> + RXAUI_MODE,
> + XPCS0_XPCS1_MODE,
> + SATA0_SATA1_MODE,
> +};
> +
> +enum aggregated_mode {
> + PHY_SL_MODE,
> + PHY_DL_MODE,
> +};
> +
> +struct intel_combo_phy;
> +
> +struct intel_cbphy_iphy {
> + struct phy *phy;
> + struct intel_combo_phy *parent;
> + struct reset_control *app_rst;
> + u32 id;
> +};
> +
> +struct intel_combo_phy {
> + struct device *dev;
> + struct clk *core_clk;
> + unsigned long clk_rate;
> + void __iomem *app_base;
> + void __iomem *cr_base;
> + struct regmap *syscfg;
> + struct regmap *hsiocfg;
> + u32 id;
> + u32 bid;
> + struct reset_control *phy_rst;
> + struct reset_control *core_rst;
> + struct intel_cbphy_iphy iphy[PHY_MAX_NUM];
> + enum intel_phy_mode phy_mode;
> + enum aggregated_mode aggr_mode;
> + u32 init_cnt;
> + struct mutex lock;
> +};
> +
> +static int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set)
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id);
> + u32 val;
> +
> + /* Register: 0 is enable, 1 is disable */
> + val = set ? 0 : mask;
> +
> + return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid),
> + mask, val);
> +}
> +
> +static int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set)
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + u32 mask = BIT(cbphy->id * 2 + iphy->id);
> + u32 val;
> +
> + /* Register: 0 is enable, 1 is disable */
> + val = set ? 0 : mask;
> +
> + return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val);
> +}
> +
> +static inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg,
> + u32 mask, u32 val)
> +{
> + u32 reg_val;
> +
> + reg_val = readl(base + reg);
> + reg_val &= ~mask;
> + reg_val |= FIELD_PREP(mask, val);
> + writel(reg_val, base + reg);
> +}
> +
> +static int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy,
> + int (*phy_cfg)(struct intel_cbphy_iphy *))
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + ret = phy_cfg(iphy);
> + if (ret)
> + return ret;
> +
> + if (cbphy->aggr_mode != PHY_DL_MODE)
> + return 0;
> +
> + return phy_cfg(&cbphy->iphy[PHY_1]);
> +}
> +
> +static int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy)
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + ret = intel_cbphy_pcie_refclk_cfg(iphy, true);
> + if (ret) {
> + dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n");
> + return ret;
> + }
> +
> + if (cbphy->init_cnt)
> + return 0;
> +
> + combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
> + PCIE_PHY_CLK_PAD, 0);
> +
> + /* Delay for stable clock PLL */
> + usleep_range(50, 100);
> +
> + return 0;
> +}
> +
> +static int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy)
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + ret = intel_cbphy_pcie_refclk_cfg(iphy, false);
> + if (ret) {
> + dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n");
> + return ret;
> + }
> +
> + if (cbphy->init_cnt)
> + return 0;
> +
> + combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
> + PCIE_PHY_CLK_PAD, 1);
> +
> + return 0;
> +}
> +
> +static int intel_cbphy_set_mode(struct intel_combo_phy *cbphy)
> +{
> + enum intel_combo_mode cb_mode = PHY_PCIE_MODE;
> + enum aggregated_mode aggr = cbphy->aggr_mode;
> + struct device *dev = cbphy->dev;
> + enum intel_phy_mode mode;
> + int ret;
> +
> + mode = cbphy->phy_mode;
> +
> + switch (mode) {
> + case PHY_PCIE_MODE:
> + cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE;
> + break;
> +
> + case PHY_XPCS_MODE:
> + cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE;
> + break;
> +
> + case PHY_SATA_MODE:
> + if (aggr == PHY_DL_MODE) {
> + dev_err(dev, "Mode:%u not support dual lane!\n", mode);
> + return -EINVAL;
> + }
> +
> + cb_mode = SATA0_SATA1_MODE;
> + break;
> + }
> +
> + ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode);
> + if (ret)
> + dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret);
> +
> + return ret;
> +}
> +
> +static void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy)
> +{
> + reset_control_assert(cbphy->core_rst);
> + reset_control_assert(cbphy->phy_rst);
> +}
> +
> +static void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy)
> +{
> + reset_control_deassert(cbphy->core_rst);
> + reset_control_deassert(cbphy->phy_rst);
> + /* Delay to ensure reset process is done */
> + usleep_range(10, 20);
> +}
> +
> +static int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy)
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + if (!cbphy->init_cnt) {
> + ret = clk_prepare_enable(cbphy->core_clk);
> + if (ret) {
> + dev_err(cbphy->dev, "Clock enable failed!\n");
> + return ret;
> + }
> +
> + ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate);
> + if (ret) {
> + dev_err(cbphy->dev, "Clock freq set to %lu failed!\n",
> + cbphy->clk_rate);
> + goto clk_err;
> + }
> +
> + intel_cbphy_rst_assert(cbphy);
> + intel_cbphy_rst_deassert(cbphy);
> + ret = intel_cbphy_set_mode(cbphy);
> + if (ret)
> + goto clk_err;
> + }
> +
> + ret = intel_cbphy_iphy_enable(iphy, true);
> + if (ret) {
> + dev_err(cbphy->dev, "Failed enabling PHY core\n");
> + goto clk_err;
> + }
> +
> + ret = reset_control_deassert(iphy->app_rst);
> + if (ret) {
> + dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n",
> + COMBO_PHY_ID(iphy), PHY_ID(iphy));
> + goto clk_err;
> + }
> +
> + /* Delay to ensure reset process is done */
> + udelay(1);
> +
> + return 0;
> +
> +clk_err:
> + clk_disable_unprepare(cbphy->core_clk);
> +
> + return ret;
> +}
> +
> +static int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy)
> +{
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + ret = reset_control_assert(iphy->app_rst);
> + if (ret) {
> + dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n",
> + COMBO_PHY_ID(iphy), PHY_ID(iphy));
> + return ret;
> + }
> +
> + ret = intel_cbphy_iphy_enable(iphy, false);
> + if (ret) {
> + dev_err(cbphy->dev, "Failed disabling PHY core\n");
> + return ret;
> + }
> +
> + if (cbphy->init_cnt)
> + return 0;
> +
> + clk_disable_unprepare(cbphy->core_clk);
> + intel_cbphy_rst_assert(cbphy);
> +
> + return 0;
> +}
> +
> +static int intel_cbphy_init(struct phy *phy)
> +{
> + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + mutex_lock(&cbphy->lock);
> + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on);
> + if (ret)
> + goto err;
> +
> + if (cbphy->phy_mode == PHY_PCIE_MODE) {
> + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk);
> + if (ret)
> + goto err;
> + }
> +
> + cbphy->init_cnt++;
> +
> +err:
> + mutex_unlock(&cbphy->lock);
> +
> + return ret;
> +}
> +
> +static int intel_cbphy_exit(struct phy *phy)
> +{
> + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
> + struct intel_combo_phy *cbphy = iphy->parent;
> + int ret;
> +
> + mutex_lock(&cbphy->lock);
> + cbphy->init_cnt--;
> + if (cbphy->phy_mode == PHY_PCIE_MODE) {
> + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk);
> + if (ret)
> + goto err;
> + }
> +
> + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off);
> +
> +err:
> + mutex_unlock(&cbphy->lock);
> +
> + return ret;
> +}
> +
> +static int intel_cbphy_calibrate(struct phy *phy)
> +{
> + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
> + struct intel_combo_phy *cbphy = iphy->parent;
> + void __iomem *cr_base = cbphy->cr_base;
> + int val, ret, id;
> +
> + if (cbphy->phy_mode != PHY_XPCS_MODE)
> + return 0;
> +
> + id = PHY_ID(iphy);
> +
> + /* trigger auto RX adaptation */
> + combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
> + ADAPT_REQ_MSK, 3);
> + /* Wait RX adaptation to finish */
> + ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id),
> + val, val & RX_ADAPT_ACK_BIT, 10, 5000);
> + if (ret)
> + dev_err(cbphy->dev, "RX Adaptation failed!\n");
> + else
> + dev_dbg(cbphy->dev, "RX Adaptation success!\n");
> +
> + /* Stop RX adaptation */
> + combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
> + ADAPT_REQ_MSK, 0);
> +
> + return ret;
> +}
> +
> +static int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy)
> +{
> + struct device *dev = cbphy->dev;
> + struct platform_device *pdev = to_platform_device(dev);
> + struct fwnode_handle *fwnode = dev_fwnode(dev);
> + struct fwnode_reference_args ref;
> + int ret;
> + u32 val;
> +
> + cbphy->core_clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(cbphy->core_clk)) {
> + ret = PTR_ERR(cbphy->core_clk);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Get clk failed:%d!\n", ret);
> + return ret;
> + }
> +
> + cbphy->core_rst = devm_reset_control_get_optional(dev, "core");
> + if (IS_ERR(cbphy->core_rst)) {
> + ret = PTR_ERR(cbphy->core_rst);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Get core reset control err: %d!\n", ret);
> + return ret;
> + }
> +
> + cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy");
> + if (IS_ERR(cbphy->phy_rst)) {
> + ret = PTR_ERR(cbphy->phy_rst);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Get PHY reset control err: %d!\n", ret);
> + return ret;
> + }
> +
> + cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0");
> + if (IS_ERR(cbphy->iphy[0].app_rst)) {
> + ret = PTR_ERR(cbphy->iphy[0].app_rst);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Get phy0 reset control err: %d!\n", ret);
> + return ret;
> + }
> +
> + cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1");
> + if (IS_ERR(cbphy->iphy[1].app_rst)) {
> + ret = PTR_ERR(cbphy->iphy[1].app_rst);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Get phy1 reset control err: %d!\n", ret);
> + return ret;
> + }
> +
> + cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app");
> + if (IS_ERR(cbphy->app_base))
> + return PTR_ERR(cbphy->app_base);
> +
> + cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core");
> + if (IS_ERR(cbphy->cr_base))
> + return PTR_ERR(cbphy->cr_base);
> +
> + /*
> + * syscfg and hsiocfg variables stores the handle of the registers set
> + * in which ComboPhy subsytem specific registers are subset. Using
> + * Register map framework to access the registers set.
> + */
> + ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL,
> + 1, 0, &ref);
> + if (ret < 0)
> + return ret;
> +
> + cbphy->id = ref.args[0];
> + cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode));
> + fwnode_handle_put(ref.fwnode);
> +
> + ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1,
> + 0, &ref);
> + if (ret < 0)
> + return ret;
> +
> + cbphy->bid = ref.args[0];
> + cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode));
> + fwnode_handle_put(ref.fwnode);
> +
> + ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1);
> + if (ret)
> + return ret;
> +
> + switch (val) {
> + case PHY_TYPE_PCIE:
> + cbphy->phy_mode = PHY_PCIE_MODE;
> + break;
> +
> + case PHY_TYPE_SATA:
> + cbphy->phy_mode = PHY_SATA_MODE;
> + break;
> +
> + case PHY_TYPE_XPCS:
> + cbphy->phy_mode = PHY_XPCS_MODE;
> + break;
> +
> + default:
> + dev_err(dev, "Invalid PHY mode: %u\n", val);
> + return -EINVAL;
> + }
> +
> + cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode];
> +
> + if (fwnode_property_present(fwnode, "intel,aggregation"))
> + cbphy->aggr_mode = PHY_DL_MODE;
> + else
> + cbphy->aggr_mode = PHY_SL_MODE;
> +
> + return 0;
> +}
> +
> +static const struct phy_ops intel_cbphy_ops = {
> + .init = intel_cbphy_init,
> + .exit = intel_cbphy_exit,
> + .calibrate = intel_cbphy_calibrate,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct phy *intel_cbphy_xlate(struct device *dev,
> + struct of_phandle_args *args)
> +{
> + struct intel_combo_phy *cbphy = dev_get_drvdata(dev);
> + u32 iphy_id;
> +
> + if (args->args_count < 1) {
> + dev_err(dev, "Invalid number of arguments\n");
> + return ERR_PTR(-EINVAL);
> + }
> +
> + iphy_id = args->args[0];
> + if (iphy_id >= PHY_MAX_NUM) {
> + dev_err(dev, "Invalid phy instance %d\n", iphy_id);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) {
> + dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return cbphy->iphy[iphy_id].phy;
> +}
> +
> +static int intel_cbphy_create(struct intel_combo_phy *cbphy)
> +{
> + struct phy_provider *phy_provider;
> + struct device *dev = cbphy->dev;
> + struct intel_cbphy_iphy *iphy;
> + int i;
> +
> + for (i = 0; i < PHY_MAX_NUM; i++) {
> + iphy = &cbphy->iphy[i];
> + iphy->parent = cbphy;
> + iphy->id = i;
> +
> + /* In dual lane mode skip phy creation for the second phy */
> + if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1)
> + continue;
> +
> + iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops);
> + if (IS_ERR(iphy->phy)) {
> + dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n",
> + COMBO_PHY_ID(iphy), PHY_ID(iphy));
> +
> + return PTR_ERR(iphy->phy);
> + }
> +
> + phy_set_drvdata(iphy->phy, iphy);
> + }
> +
> + dev_set_drvdata(dev, cbphy);
> + phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate);
> + if (IS_ERR(phy_provider))
> + dev_err(dev, "Register PHY provider failed!\n");
> +
> + return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static int intel_cbphy_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct intel_combo_phy *cbphy;
> + int ret;
> +
> + cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL);
> + if (!cbphy)
> + return -ENOMEM;
> +
> + cbphy->dev = dev;
> + cbphy->init_cnt = 0;
> + mutex_init(&cbphy->lock);
> + ret = intel_cbphy_fwnode_parse(cbphy);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, cbphy);
> +
> + return intel_cbphy_create(cbphy);
> +}
> +
> +static int intel_cbphy_remove(struct platform_device *pdev)
> +{
> + struct intel_combo_phy *cbphy = platform_get_drvdata(pdev);
> +
> + intel_cbphy_rst_assert(cbphy);
> + clk_disable_unprepare(cbphy->core_clk);
> + return 0;
> +}
> +
> +static const struct of_device_id of_intel_cbphy_match[] = {
> + { .compatible = "intel,combo-phy" },
> + { .compatible = "intel,combophy-lgm" },
> + {}
> +};
> +
> +static struct platform_driver intel_cbphy_driver = {
> + .probe = intel_cbphy_probe,
> + .remove = intel_cbphy_remove,
> + .driver = {
> + .name = "intel-combo-phy",
> + .of_match_table = of_intel_cbphy_match,
> + }
> +};
> +
> +module_platform_driver(intel_cbphy_driver);
> +
> +MODULE_DESCRIPTION("Intel Combo-phy driver");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* Re: [PATCH 4/4] arm64: dts: mt6797: Fix mmsys node name
From: Chun-Kuang Hu @ 2020-05-19 15:06 UTC (permalink / raw)
To: matthias.bgg
Cc: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd,
Kate Stewart, devicetree, Greg Kroah-Hartman, linux-kernel,
moderated list:ARM/Mediatek SoC support, Linux ARM, mtk01761,
Thomas Gleixner, linux-clk, Allison Randal
In-Reply-To: <20200518113156.25009-4-matthias.bgg@kernel.org>
Hi, Matthias:
<matthias.bgg@kernel.org> 於 2020年5月18日 週一 下午7:33寫道:
>
> From: Matthias Brugger <matthias.bgg@gmail.com>
>
> Node names are supposed to match the class of the device. The
> mmsys node is a syscon as it provides more then just a clock controller.
> Update the name.
Reviewed-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
>
> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
>
> ---
>
> arch/arm64/boot/dts/mediatek/mt6797.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt6797.dtsi b/arch/arm64/boot/dts/mediatek/mt6797.dtsi
> index 136ef9527a0d..3efd032481ce 100644
> --- a/arch/arm64/boot/dts/mediatek/mt6797.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt6797.dtsi
> @@ -233,7 +233,7 @@ uart3: serial@11005000 {
> status = "disabled";
> };
>
> - mmsys: mmsys_config@14000000 {
> + mmsys: syscon@14000000 {
> compatible = "mediatek,mt6797-mmsys", "syscon";
> reg = <0 0x14000000 0 0x1000>;
> #clock-cells = <1>;
> --
> 2.26.2
>
>
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox