1854519fdSIan Lepore /*-
2854519fdSIan Lepore * Copyright (c) 2017 Rogiel Sulzbach <rogiel@allogica.com>
3854519fdSIan Lepore * All rights reserved.
4854519fdSIan Lepore *
5854519fdSIan Lepore * Redistribution and use in source and binary forms, with or without
6854519fdSIan Lepore * modification, are permitted provided that the following conditions
7854519fdSIan Lepore * are met:
8854519fdSIan Lepore * 1. Redistributions of source code must retain the above copyright
9854519fdSIan Lepore * notice, this list of conditions and the following disclaimer.
10854519fdSIan Lepore * 2. Redistributions in binary form must reproduce the above copyright
11854519fdSIan Lepore * notice, this list of conditions and the following disclaimer in the
12854519fdSIan Lepore * documentation and/or other materials provided with the distribution.
13854519fdSIan Lepore *
14854519fdSIan Lepore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15854519fdSIan Lepore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16854519fdSIan Lepore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17854519fdSIan Lepore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18854519fdSIan Lepore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19854519fdSIan Lepore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20854519fdSIan Lepore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21854519fdSIan Lepore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22854519fdSIan Lepore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23854519fdSIan Lepore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24854519fdSIan Lepore * SUCH DAMAGE.
25854519fdSIan Lepore */
26854519fdSIan Lepore
27854519fdSIan Lepore
28854519fdSIan Lepore #include <sys/param.h>
29854519fdSIan Lepore #include <sys/systm.h>
30854519fdSIan Lepore #include <sys/bus.h>
31854519fdSIan Lepore #include <sys/rman.h>
32854519fdSIan Lepore #include <sys/kernel.h>
33854519fdSIan Lepore #include <sys/module.h>
34854519fdSIan Lepore
35854519fdSIan Lepore #include <machine/bus.h>
36854519fdSIan Lepore #include <dev/ofw/ofw_bus.h>
37854519fdSIan Lepore #include <dev/ofw/ofw_bus_subr.h>
38854519fdSIan Lepore
39854519fdSIan Lepore #include <dev/ahci/ahci.h>
40854519fdSIan Lepore #include <arm/freescale/imx/imx_iomuxreg.h>
41854519fdSIan Lepore #include <arm/freescale/imx/imx_iomuxvar.h>
42854519fdSIan Lepore #include <arm/freescale/imx/imx_ccmvar.h>
43854519fdSIan Lepore
44854519fdSIan Lepore #define SATA_TIMER1MS 0x000000e0
45854519fdSIan Lepore
46854519fdSIan Lepore #define SATA_P0PHYCR 0x00000178
47854519fdSIan Lepore #define SATA_P0PHYCR_CR_READ (1 << 19)
48854519fdSIan Lepore #define SATA_P0PHYCR_CR_WRITE (1 << 18)
49854519fdSIan Lepore #define SATA_P0PHYCR_CR_CAP_DATA (1 << 17)
50854519fdSIan Lepore #define SATA_P0PHYCR_CR_CAP_ADDR (1 << 16)
51854519fdSIan Lepore #define SATA_P0PHYCR_CR_DATA_IN(v) ((v) & 0xffff)
52854519fdSIan Lepore
53854519fdSIan Lepore #define SATA_P0PHYSR 0x0000017c
54854519fdSIan Lepore #define SATA_P0PHYSR_CR_ACK (1 << 18)
55854519fdSIan Lepore #define SATA_P0PHYSR_CR_DATA_OUT(v) ((v) & 0xffff)
56854519fdSIan Lepore
57854519fdSIan Lepore /* phy registers */
58854519fdSIan Lepore #define SATA_PHY_CLOCK_RESET 0x7f3f
59854519fdSIan Lepore #define SATA_PHY_CLOCK_RESET_RST (1 << 0)
60854519fdSIan Lepore
61854519fdSIan Lepore #define SATA_PHY_LANE0_OUT_STAT 0x2003
62854519fdSIan Lepore #define SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE (1 << 1)
63854519fdSIan Lepore
64cf2cec68SIan Lepore static struct ofw_compat_data compat_data[] = {
65cf2cec68SIan Lepore {"fsl,imx6q-ahci", true},
66cf2cec68SIan Lepore {NULL, false}
67cf2cec68SIan Lepore };
68cf2cec68SIan Lepore
69854519fdSIan Lepore static int
imx6_ahci_phy_ctrl(struct ahci_controller * sc,uint32_t bitmask,bool on)70854519fdSIan Lepore imx6_ahci_phy_ctrl(struct ahci_controller* sc, uint32_t bitmask, bool on)
71854519fdSIan Lepore {
72854519fdSIan Lepore uint32_t v;
73854519fdSIan Lepore int timeout;
74854519fdSIan Lepore bool state;
75854519fdSIan Lepore
76854519fdSIan Lepore v = ATA_INL(sc->r_mem, SATA_P0PHYCR);
77854519fdSIan Lepore if (on) {
78854519fdSIan Lepore v |= bitmask;
79854519fdSIan Lepore } else {
80854519fdSIan Lepore v &= ~bitmask;
81854519fdSIan Lepore }
82854519fdSIan Lepore ATA_OUTL(sc->r_mem, SATA_P0PHYCR, v);
83854519fdSIan Lepore
84854519fdSIan Lepore for (timeout = 5000; timeout > 0; --timeout) {
85854519fdSIan Lepore v = ATA_INL(sc->r_mem, SATA_P0PHYSR);
86854519fdSIan Lepore state = (v & SATA_P0PHYSR_CR_ACK) == SATA_P0PHYSR_CR_ACK;
87854519fdSIan Lepore if(state == on) {
88854519fdSIan Lepore break;
89854519fdSIan Lepore }
90854519fdSIan Lepore DELAY(100);
91854519fdSIan Lepore }
92854519fdSIan Lepore
93854519fdSIan Lepore if (timeout > 0) {
94854519fdSIan Lepore return (0);
95854519fdSIan Lepore }
96854519fdSIan Lepore
97854519fdSIan Lepore return (ETIMEDOUT);
98854519fdSIan Lepore }
99854519fdSIan Lepore
100854519fdSIan Lepore static int
imx6_ahci_phy_addr(struct ahci_controller * sc,uint32_t addr)101854519fdSIan Lepore imx6_ahci_phy_addr(struct ahci_controller* sc, uint32_t addr)
102854519fdSIan Lepore {
103854519fdSIan Lepore int error;
104854519fdSIan Lepore
105854519fdSIan Lepore DELAY(100);
106854519fdSIan Lepore
107854519fdSIan Lepore ATA_OUTL(sc->r_mem, SATA_P0PHYCR, addr);
108854519fdSIan Lepore
109854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, true);
110854519fdSIan Lepore if (error != 0) {
111854519fdSIan Lepore device_printf(sc->dev,
112854519fdSIan Lepore "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=1\n",
113854519fdSIan Lepore __FUNCTION__);
114854519fdSIan Lepore return (error);
115854519fdSIan Lepore }
116854519fdSIan Lepore
117854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, false);
118854519fdSIan Lepore if (error != 0) {
119854519fdSIan Lepore device_printf(sc->dev,
120854519fdSIan Lepore "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=0\n",
121854519fdSIan Lepore __FUNCTION__);
122854519fdSIan Lepore return (error);
123854519fdSIan Lepore }
124854519fdSIan Lepore
125854519fdSIan Lepore return (0);
126854519fdSIan Lepore }
127854519fdSIan Lepore
128854519fdSIan Lepore static int
imx6_ahci_phy_write(struct ahci_controller * sc,uint32_t addr,uint16_t data)129854519fdSIan Lepore imx6_ahci_phy_write(struct ahci_controller* sc, uint32_t addr,
130854519fdSIan Lepore uint16_t data)
131854519fdSIan Lepore {
132854519fdSIan Lepore int error;
133854519fdSIan Lepore
134854519fdSIan Lepore error = imx6_ahci_phy_addr(sc, addr);
135854519fdSIan Lepore if (error != 0) {
136854519fdSIan Lepore device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n",
137854519fdSIan Lepore __FUNCTION__);
138854519fdSIan Lepore return (error);
139854519fdSIan Lepore }
140854519fdSIan Lepore
141854519fdSIan Lepore ATA_OUTL(sc->r_mem, SATA_P0PHYCR, data);
142854519fdSIan Lepore
143854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, true);
144854519fdSIan Lepore if (error != 0) {
145854519fdSIan Lepore device_printf(sc->dev,
146854519fdSIan Lepore "%s: error on SATA_P0PHYCR_CR_CAP_DATA=1\n", __FUNCTION__);
147854519fdSIan Lepore return (error);
148854519fdSIan Lepore }
149854519fdSIan Lepore if (imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, false) != 0) {
150854519fdSIan Lepore device_printf(sc->dev,
151854519fdSIan Lepore "%s: error on SATA_P0PHYCR_CR_CAP_DATA=0\n", __FUNCTION__);
152854519fdSIan Lepore return (error);
153854519fdSIan Lepore }
154854519fdSIan Lepore
155854519fdSIan Lepore if ((addr == SATA_PHY_CLOCK_RESET) && data) {
156854519fdSIan Lepore /* we can't check ACK after RESET */
157854519fdSIan Lepore ATA_OUTL(sc->r_mem, SATA_P0PHYCR,
158854519fdSIan Lepore SATA_P0PHYCR_CR_DATA_IN(data) | SATA_P0PHYCR_CR_WRITE);
159854519fdSIan Lepore return (0);
160854519fdSIan Lepore }
161854519fdSIan Lepore
162854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, true);
163854519fdSIan Lepore if (error != 0) {
164854519fdSIan Lepore device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=1\n",
165854519fdSIan Lepore __FUNCTION__);
166854519fdSIan Lepore return (error);
167854519fdSIan Lepore }
168854519fdSIan Lepore
169854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, false);
170854519fdSIan Lepore if (error != 0) {
171854519fdSIan Lepore device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=0\n",
172854519fdSIan Lepore __FUNCTION__);
173854519fdSIan Lepore return (error);
174854519fdSIan Lepore }
175854519fdSIan Lepore
176854519fdSIan Lepore return (0);
177854519fdSIan Lepore }
178854519fdSIan Lepore
179854519fdSIan Lepore static int
imx6_ahci_phy_read(struct ahci_controller * sc,uint32_t addr,uint16_t * val)180854519fdSIan Lepore imx6_ahci_phy_read(struct ahci_controller* sc, uint32_t addr, uint16_t* val)
181854519fdSIan Lepore {
182854519fdSIan Lepore int error;
183854519fdSIan Lepore uint32_t v;
184854519fdSIan Lepore
185854519fdSIan Lepore error = imx6_ahci_phy_addr(sc, addr);
186854519fdSIan Lepore if (error != 0) {
187854519fdSIan Lepore device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n",
188854519fdSIan Lepore __FUNCTION__);
189854519fdSIan Lepore return (error);
190854519fdSIan Lepore }
191854519fdSIan Lepore
192854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, true);
193854519fdSIan Lepore if (error != 0) {
194854519fdSIan Lepore device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=1\n",
195854519fdSIan Lepore __FUNCTION__);
196854519fdSIan Lepore return (error);
197854519fdSIan Lepore }
198854519fdSIan Lepore
199854519fdSIan Lepore v = ATA_INL(sc->r_mem, SATA_P0PHYSR);
200854519fdSIan Lepore
201854519fdSIan Lepore error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, false);
202854519fdSIan Lepore if (error != 0) {
203854519fdSIan Lepore device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=0\n",
204854519fdSIan Lepore __FUNCTION__);
205854519fdSIan Lepore return (error);
206854519fdSIan Lepore }
207854519fdSIan Lepore
208854519fdSIan Lepore *val = SATA_P0PHYSR_CR_DATA_OUT(v);
209854519fdSIan Lepore return (0);
210854519fdSIan Lepore }
211854519fdSIan Lepore
212854519fdSIan Lepore static int
imx6_ahci_probe(device_t dev)213854519fdSIan Lepore imx6_ahci_probe(device_t dev)
214854519fdSIan Lepore {
215854519fdSIan Lepore
216854519fdSIan Lepore if (!ofw_bus_status_okay(dev)) {
217854519fdSIan Lepore return (ENXIO);
218854519fdSIan Lepore }
219854519fdSIan Lepore
220cf2cec68SIan Lepore if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
221854519fdSIan Lepore return (ENXIO);
222854519fdSIan Lepore }
223854519fdSIan Lepore device_set_desc(dev, "i.MX6 Integrated AHCI controller");
224854519fdSIan Lepore
225854519fdSIan Lepore return (BUS_PROBE_DEFAULT);
226854519fdSIan Lepore }
227854519fdSIan Lepore
228854519fdSIan Lepore static int
imx6_ahci_attach(device_t dev)229854519fdSIan Lepore imx6_ahci_attach(device_t dev)
230854519fdSIan Lepore {
231854519fdSIan Lepore struct ahci_controller* ctlr;
232854519fdSIan Lepore uint16_t pllstat;
233854519fdSIan Lepore uint32_t v;
234854519fdSIan Lepore int error, timeout;
235854519fdSIan Lepore
236854519fdSIan Lepore ctlr = device_get_softc(dev);
237854519fdSIan Lepore
238854519fdSIan Lepore /* Power up the controller and phy. */
239854519fdSIan Lepore error = imx6_ccm_sata_enable();
240854519fdSIan Lepore if (error != 0) {
241854519fdSIan Lepore device_printf(dev, "error enabling controller and phy\n");
242854519fdSIan Lepore return (error);
243854519fdSIan Lepore }
244854519fdSIan Lepore
245854519fdSIan Lepore ctlr->vendorid = 0;
246854519fdSIan Lepore ctlr->deviceid = 0;
247854519fdSIan Lepore ctlr->subvendorid = 0;
248854519fdSIan Lepore ctlr->subdeviceid = 0;
249854519fdSIan Lepore ctlr->numirqs = 1;
250854519fdSIan Lepore ctlr->r_rid = 0;
251854519fdSIan Lepore if ((ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
252854519fdSIan Lepore &ctlr->r_rid, RF_ACTIVE)) == NULL) {
253854519fdSIan Lepore return (ENXIO);
254854519fdSIan Lepore }
255854519fdSIan Lepore
256854519fdSIan Lepore v = imx_iomux_gpr_get(IOMUX_GPR13);
257854519fdSIan Lepore /* Clear out existing values; these numbers are bitmasks. */
258854519fdSIan Lepore v &= ~(IOMUX_GPR13_SATA_PHY_8(7) |
259854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_7(0x1f) |
260854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_6(7) |
261854519fdSIan Lepore IOMUX_GPR13_SATA_SPEED(1) |
262854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_5(1) |
263854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_4(7) |
264854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_3(0xf) |
265854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_2(0x1f) |
266854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_1(1) |
267854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_0(1));
268854519fdSIan Lepore /* setting */
269854519fdSIan Lepore v |= IOMUX_GPR13_SATA_PHY_8(5) | /* Rx 3.0db */
270854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_7(0x12) | /* Rx SATA2m */
271854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_6(3) | /* Rx DPLL mode */
272854519fdSIan Lepore IOMUX_GPR13_SATA_SPEED(1) | /* 3.0GHz */
273854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_5(0) | /* SpreadSpectram */
274854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_4(4) | /* Tx Attenuation 9/16 */
275854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_3(0) | /* Tx Boost 0db */
276854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_2(0x11) | /* Tx Level 1.104V */
277854519fdSIan Lepore IOMUX_GPR13_SATA_PHY_1(1); /* PLL clock enable */
278854519fdSIan Lepore imx_iomux_gpr_set(IOMUX_GPR13, v);
279854519fdSIan Lepore
280854519fdSIan Lepore /* phy reset */
281854519fdSIan Lepore error = imx6_ahci_phy_write(ctlr, SATA_PHY_CLOCK_RESET,
282854519fdSIan Lepore SATA_PHY_CLOCK_RESET_RST);
283854519fdSIan Lepore if (error != 0) {
284854519fdSIan Lepore device_printf(dev, "cannot reset PHY\n");
285854519fdSIan Lepore goto fail;
286854519fdSIan Lepore }
287854519fdSIan Lepore
288854519fdSIan Lepore for (timeout = 50; timeout > 0; --timeout) {
289854519fdSIan Lepore DELAY(100);
290854519fdSIan Lepore error = imx6_ahci_phy_read(ctlr, SATA_PHY_LANE0_OUT_STAT,
291854519fdSIan Lepore &pllstat);
292854519fdSIan Lepore if (error != 0) {
293854519fdSIan Lepore device_printf(dev, "cannot read LANE0 status\n");
294854519fdSIan Lepore goto fail;
295854519fdSIan Lepore }
296854519fdSIan Lepore if (pllstat & SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE) {
297854519fdSIan Lepore break;
298854519fdSIan Lepore }
299854519fdSIan Lepore }
300854519fdSIan Lepore if (timeout <= 0) {
301854519fdSIan Lepore device_printf(dev, "time out reading LANE0 status\n");
302854519fdSIan Lepore error = ETIMEDOUT;
303854519fdSIan Lepore goto fail;
304854519fdSIan Lepore }
305854519fdSIan Lepore
306854519fdSIan Lepore /* Support Staggered Spin-up */
307854519fdSIan Lepore v = ATA_INL(ctlr->r_mem, AHCI_CAP);
308854519fdSIan Lepore ATA_OUTL(ctlr->r_mem, AHCI_CAP, v | AHCI_CAP_SSS);
309854519fdSIan Lepore
310854519fdSIan Lepore /* Ports Implemented. must set 1 */
311854519fdSIan Lepore v = ATA_INL(ctlr->r_mem, AHCI_PI);
312854519fdSIan Lepore ATA_OUTL(ctlr->r_mem, AHCI_PI, v | (1 << 0));
313854519fdSIan Lepore
314854519fdSIan Lepore /* set 1ms-timer = AHB clock / 1000 */
315854519fdSIan Lepore ATA_OUTL(ctlr->r_mem, SATA_TIMER1MS,
316854519fdSIan Lepore imx_ccm_ahb_hz() / 1000);
317854519fdSIan Lepore
318854519fdSIan Lepore /*
319854519fdSIan Lepore * Note: ahci_attach will release ctlr->r_mem on errors automatically
320854519fdSIan Lepore */
321854519fdSIan Lepore return (ahci_attach(dev));
322854519fdSIan Lepore
323854519fdSIan Lepore fail:
324854519fdSIan Lepore bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem);
325854519fdSIan Lepore return (error);
326854519fdSIan Lepore }
327854519fdSIan Lepore
328854519fdSIan Lepore static int
imx6_ahci_detach(device_t dev)329854519fdSIan Lepore imx6_ahci_detach(device_t dev)
330854519fdSIan Lepore {
331854519fdSIan Lepore
332854519fdSIan Lepore return (ahci_detach(dev));
333854519fdSIan Lepore }
334854519fdSIan Lepore
335854519fdSIan Lepore static device_method_t imx6_ahci_ata_methods[] = {
336854519fdSIan Lepore /* device probe, attach and detach methods */
337854519fdSIan Lepore DEVMETHOD(device_probe, imx6_ahci_probe),
338854519fdSIan Lepore DEVMETHOD(device_attach, imx6_ahci_attach),
339854519fdSIan Lepore DEVMETHOD(device_detach, imx6_ahci_detach),
340854519fdSIan Lepore
341854519fdSIan Lepore /* ahci bus methods */
342854519fdSIan Lepore DEVMETHOD(bus_print_child, ahci_print_child),
343854519fdSIan Lepore DEVMETHOD(bus_alloc_resource, ahci_alloc_resource),
344854519fdSIan Lepore DEVMETHOD(bus_release_resource, ahci_release_resource),
345854519fdSIan Lepore DEVMETHOD(bus_setup_intr, ahci_setup_intr),
346854519fdSIan Lepore DEVMETHOD(bus_teardown_intr, ahci_teardown_intr),
347ddfc9c4cSWarner Losh DEVMETHOD(bus_child_location, ahci_child_location),
348854519fdSIan Lepore
349854519fdSIan Lepore DEVMETHOD_END
350854519fdSIan Lepore };
351854519fdSIan Lepore
352854519fdSIan Lepore static driver_t ahci_ata_driver = {
353854519fdSIan Lepore "ahci",
354854519fdSIan Lepore imx6_ahci_ata_methods,
355854519fdSIan Lepore sizeof(struct ahci_controller)
356854519fdSIan Lepore };
357854519fdSIan Lepore
358*23802d41SJohn Baldwin DRIVER_MODULE(imx6_ahci, simplebus, ahci_ata_driver, 0, 0);
359cf2cec68SIan Lepore MODULE_DEPEND(imx6_ahci, ahci, 1, 1, 1);
360cf2cec68SIan Lepore SIMPLEBUS_PNP_INFO(compat_data)
361