On 12/11/2019 1:48 AM, Philipp Zabel wrote: >> +#define BRCM_RESCAL_START 0 >> +#define BRCM_RESCAL_START_BIT BIT(0) >> +#define BRCM_RESCAL_CTRL 4 >> +#define BRCM_RESCAL_STATUS 8 >> +#define BRCM_RESCAL_STATUS_BIT BIT(0) > > Is there any reason the start bit is indented but the status bit is not? This is a convention we have tried to adopt to denote the definition from a register word address/offset versus the definition for bits within that register word. > >> + >> +struct brcm_rescal_reset { >> + void __iomem *base; >> + struct device *dev; >> + struct reset_controller_dev rcdev; >> +}; >> + >> +static int brcm_rescal_reset_assert(struct reset_controller_dev *rcdev, >> + unsigned long id) >> +{ >> + return 0; >> +} > > Please do not implement the assert operation if it doesn't cause a reset > line to be asserted afterwards. > The reset core will return 0 from reset_control_assert() for shared > reset controls if .assert is not implemented. OK, will drop it. > >> + >> +static int brcm_rescal_reset_deassert(struct reset_controller_dev *rcdev, >> + unsigned long id) >> +{ >> + struct brcm_rescal_reset *data = >> + container_of(rcdev, struct brcm_rescal_reset, rcdev); >> + void __iomem *base = data->base; >> + const int NUM_RETRIES = 10; >> + u32 reg; >> + int i; >> + >> + reg = readl(base + BRCM_RESCAL_START); >> + writel(reg | BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START); >> + reg = readl(base + BRCM_RESCAL_START); > > Are there any other fields beside the START_BIT in this register? This is the only bit actually. > >> + if (!(reg & BRCM_RESCAL_START_BIT)) { >> + dev_err(data->dev, "failed to start sata/pcie rescal\n"); >> + return -EIO; >> + } >> + >> + reg = readl(base + BRCM_RESCAL_STATUS); >> + for (i = NUM_RETRIES; i >= 0 && !(reg & BRCM_RESCAL_STATUS_BIT); i--) { >> + udelay(100); >> + reg = readl(base + BRCM_RESCAL_STATUS); >> + } > > This timeout loop should be replaced by a single readl_poll_timeout(). > At 100 µs waits per iteration this could use the sleeping variant. OK, will do. > >> + if (!(reg & BRCM_RESCAL_STATUS_BIT)) { >> + dev_err(data->dev, "timedout on sata/pcie rescal\n"); >> + return -ETIMEDOUT; >> + } >> + >> + reg = readl(base + BRCM_RESCAL_START); >> + writel(reg ^ BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START); > > Please use &= ~BRCM_RESCAL_START_BIT instead. > I think the idea was to avoid unconditionally clearing it, but based on the documentation, I don't see this being harmful, Jim? >> + reg = readl(base + BRCM_RESCAL_START); >> + dev_dbg(data->dev, "sata/pcie rescal success\n"); >> + >> + return 0; >> +} > > This whole function looks a lot like it doesn't just deassert a reset > line, but actually issues a complete reset procedure of some kind. Do > you have some insight on what actually happens in the hardware when the > start bit is triggered? I suspect this should be implemented with the > .reset operation. This hardware block is controlling the reset and calibration process of the SATA/PCIe combo PHY analog front end, but is not technically part of the PCIe or SATA PHY proper, it stands on its own, both functionally and from a register space perspective. The motivation for modelling this as a reset controller is that it does a reset (and a calibration) and this is a shared reset line among 2/3 instances of another block. If you think we should model this differently, please let us know. -- Florian