1c1733db1SRobert Mustacchi /*
2c1733db1SRobert Mustacchi * This file and its contents are supplied under the terms of the
3c1733db1SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4c1733db1SRobert Mustacchi * You may only use this file in accordance with the terms of version
5c1733db1SRobert Mustacchi * 1.0 of the CDDL.
6c1733db1SRobert Mustacchi *
7c1733db1SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8c1733db1SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9c1733db1SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10c1733db1SRobert Mustacchi */
11c1733db1SRobert Mustacchi
12c1733db1SRobert Mustacchi /*
13*9fe9c13cSRobert Mustacchi * Copyright 2026 Oxide Computer Company
14c1733db1SRobert Mustacchi */
15c1733db1SRobert Mustacchi
16c1733db1SRobert Mustacchi /*
17c1733db1SRobert Mustacchi * Intel PCH (ICH) SMBus Controller
18c1733db1SRobert Mustacchi *
19c1733db1SRobert Mustacchi * This driver supports a wide variety of controllers, having been found in
20c1733db1SRobert Mustacchi * various Intel chipsets since the late 1990s. The hardware has evolved a
21c1733db1SRobert Mustacchi * little bit, but it is a controller that only supports SMBus 2.0 and a little
22c1733db1SRobert Mustacchi * bit of I2C emulation to support EEPROMs that fit in a single address byte. It
23c1733db1SRobert Mustacchi * cannot run arbitrary I2C commands.
24c1733db1SRobert Mustacchi *
25c1733db1SRobert Mustacchi * As a result, the hardware interface is structured around issuing specific
26c1733db1SRobert Mustacchi * SMBus commands and operations. For non-block based commands this is
27c1733db1SRobert Mustacchi * straightforward. Unfortunately, for block commands it is less simple. In the
28c1733db1SRobert Mustacchi * hardware's evolution, support for a block buffer was added. Prior to this one
29c1733db1SRobert Mustacchi * has to read and write a single byte at a time. With this, one can instead use
30c1733db1SRobert Mustacchi * the 32-byte buffer for transactions. Notably, 32 bytes comes from the SMBus
31c1733db1SRobert Mustacchi * 2.0 block limit.
32c1733db1SRobert Mustacchi *
33c1733db1SRobert Mustacchi * While this 32-byte buffer is a great simplifying thing, it actually doesn't
34c1733db1SRobert Mustacchi * work with I2C emulation and therefore we have to do per-byte I/Os in the
35c1733db1SRobert Mustacchi * device. Because I2C block I/Os are much more common than SMBus, this means
36c1733db1SRobert Mustacchi * that the block buffer flag, like the I2C flag, are enabled on a per-request
37c1733db1SRobert Mustacchi * basis.
38c1733db1SRobert Mustacchi *
39c1733db1SRobert Mustacchi * When operating in the byte mode, we basically track how many bytes there are
40c1733db1SRobert Mustacchi * to transmit and receive and will issue all transmit bytes before any repeated
41c1733db1SRobert Mustacchi * start that requires reading.
42c1733db1SRobert Mustacchi */
43c1733db1SRobert Mustacchi
44c1733db1SRobert Mustacchi #include <sys/modctl.h>
45c1733db1SRobert Mustacchi #include <sys/conf.h>
46c1733db1SRobert Mustacchi #include <sys/devops.h>
47c1733db1SRobert Mustacchi #include <sys/ddi.h>
48c1733db1SRobert Mustacchi #include <sys/sunddi.h>
49c1733db1SRobert Mustacchi #include <sys/pci.h>
50c1733db1SRobert Mustacchi #include <sys/sysmacros.h>
51c1733db1SRobert Mustacchi
52c1733db1SRobert Mustacchi #include <sys/i2c/controller.h>
53c1733db1SRobert Mustacchi #include "pchsmbus.h"
54c1733db1SRobert Mustacchi
55c1733db1SRobert Mustacchi /*
56c1733db1SRobert Mustacchi * The controller only has a single BAR which contains what we need. This may be
57c1733db1SRobert Mustacchi * in I/O space or MMIO space, but which it is doesn't make a difference to the
58c1733db1SRobert Mustacchi * driver itself.
59c1733db1SRobert Mustacchi */
60c1733db1SRobert Mustacchi #define PCHSMBUS_REGNO 1
61c1733db1SRobert Mustacchi
62c1733db1SRobert Mustacchi typedef enum {
63c1733db1SRobert Mustacchi PCHSMBUS_INIT_PCI = 1 << 0,
64c1733db1SRobert Mustacchi PCHSMBUS_INIT_REGS = 1 << 1,
65c1733db1SRobert Mustacchi PCHSMBUS_INIT_INTR_ALLOC = 1 << 2,
66c1733db1SRobert Mustacchi PCHSMBUS_INIT_INTR_HDL = 1 << 3,
67c1733db1SRobert Mustacchi PCHSMBUS_INIT_SYNC = 1 << 4,
68c1733db1SRobert Mustacchi PCHSMBUS_INIT_CTRL = 1 << 5,
69c1733db1SRobert Mustacchi PCHSMBUS_INIT_INTR_EN = 1 << 6,
70c1733db1SRobert Mustacchi PCHSMBUS_INIT_I2C = 1 << 7,
71c1733db1SRobert Mustacchi /*
72c1733db1SRobert Mustacchi * The following are used at run time.
73c1733db1SRobert Mustacchi */
74c1733db1SRobert Mustacchi PCHSMBUS_RUN_BUF_EN = 1 << 8,
75c1733db1SRobert Mustacchi PCHSMBUS_RUN_I2C_EN = 1 << 9
76c1733db1SRobert Mustacchi } pchsmbus_init_t;
77c1733db1SRobert Mustacchi
78c1733db1SRobert Mustacchi typedef struct {
79c1733db1SRobert Mustacchi dev_info_t *ps_dip;
80c1733db1SRobert Mustacchi ddi_acc_handle_t ps_cfg;
81c1733db1SRobert Mustacchi pchsmbus_init_t ps_init;
82c1733db1SRobert Mustacchi pch_smbus_feat_t ps_feats;
83c1733db1SRobert Mustacchi /*
84c1733db1SRobert Mustacchi * Register related data
85c1733db1SRobert Mustacchi */
86c1733db1SRobert Mustacchi caddr_t ps_base;
87c1733db1SRobert Mustacchi off_t ps_regsize;
88c1733db1SRobert Mustacchi ddi_acc_handle_t ps_regs;
89c1733db1SRobert Mustacchi uint32_t ps_init_hcfg;
90c1733db1SRobert Mustacchi uint8_t ps_init_hctl;
91c1733db1SRobert Mustacchi uint8_t ps_init_scmd;
92c1733db1SRobert Mustacchi /*
93c1733db1SRobert Mustacchi * Interrupt data
94c1733db1SRobert Mustacchi */
95c1733db1SRobert Mustacchi int ps_nintrs;
96c1733db1SRobert Mustacchi ddi_intr_handle_t ps_intr_hdl;
97c1733db1SRobert Mustacchi uint_t ps_intr_pri;
98c1733db1SRobert Mustacchi /*
99c1733db1SRobert Mustacchi * Request and framework synchronization
100c1733db1SRobert Mustacchi */
101c1733db1SRobert Mustacchi kmutex_t ps_mutex;
102c1733db1SRobert Mustacchi kcondvar_t ps_cv;
103c1733db1SRobert Mustacchi i2c_ctrl_hdl_t *ps_hdl;
104c1733db1SRobert Mustacchi smbus_req_t *ps_req;
105c1733db1SRobert Mustacchi uint16_t ps_req_off;
106c1733db1SRobert Mustacchi uint8_t ps_req_hctl;
107c1733db1SRobert Mustacchi bool ps_req_done;
108c1733db1SRobert Mustacchi i2c_ctrl_error_t ps_kill_err;
109c1733db1SRobert Mustacchi } pchsmbus_t;
110c1733db1SRobert Mustacchi
111c1733db1SRobert Mustacchi typedef struct {
112c1733db1SRobert Mustacchi uint16_t pcm_did;
113c1733db1SRobert Mustacchi pch_smbus_feat_t pcm_feat;
114c1733db1SRobert Mustacchi } pchsmbus_hw_map_t;
115c1733db1SRobert Mustacchi
116c1733db1SRobert Mustacchi static const pchsmbus_hw_map_t pchsmbus_feats[] = {
117c1733db1SRobert Mustacchi { PCH_SMBUS_ICH0_82801AA, 0 },
118c1733db1SRobert Mustacchi { PCH_SMBUS_ICH0_82901AB, 0 },
119c1733db1SRobert Mustacchi { PCH_SMBUS_ICH2_82801BA, PCH_SMBUS_FEAT_TARG },
120c1733db1SRobert Mustacchi { PCH_SMBUS_ICH3_82801CA, PCH_SMBUS_FEAT_ALL_ICH3 },
121c1733db1SRobert Mustacchi { PCH_SMBUS_ICH4_82801DB, PCH_SMBUS_FEAT_ALL_ICH4 },
122c1733db1SRobert Mustacchi { PCH_SMBUS_ICH5_82801Ex, PCH_SMBUS_FEAT_ALL_ICH5 },
123c1733db1SRobert Mustacchi { PCH_SMBUS_6300ESB, PCH_SMBUS_FEAT_ALL_ICH5 },
124c1733db1SRobert Mustacchi { PCH_SMBUS_ICH6, PCH_SMBUS_FEAT_ALL_ICH5 },
125c1733db1SRobert Mustacchi { PCH_SMBUS_631xESB, PCH_SMBUS_FEAT_ALL_ICH5 },
126c1733db1SRobert Mustacchi { PCH_SMBUS_ICH7, PCH_SMBUS_FEAT_ALL_ICH5 },
127c1733db1SRobert Mustacchi { PCH_SMBUS_ICH8, PCH_SMBUS_FEAT_ALL_ICH8 },
128c1733db1SRobert Mustacchi { PCH_SMBUS_ICH9, PCH_SMBUS_FEAT_ALL_ICH8 },
129c1733db1SRobert Mustacchi { PCH_SMBUS_ICH10_CORP, PCH_SMBUS_FEAT_ALL_ICH8 },
130c1733db1SRobert Mustacchi { PCH_SMBUS_ICH10_USER, PCH_SMBUS_FEAT_ALL_ICH8 },
131c1733db1SRobert Mustacchi { PCH_SMBUS_PCH5, PCH_SMBUS_FEAT_ALL_ICH8 },
132c1733db1SRobert Mustacchi { PCH_SMBUS_C600, PCH_SMBUS_FEAT_ALL_ICH8 },
133c1733db1SRobert Mustacchi { PCH_SMBUS_C600_SMB0, PCH_SMBUS_FEAT_ALL_ICH8 },
134c1733db1SRobert Mustacchi { PCH_SMBUS_C600_SMB1, PCH_SMBUS_FEAT_ALL_ICH8 },
135c1733db1SRobert Mustacchi { PCH_SMBUS_C600_SMB2, PCH_SMBUS_FEAT_ALL_ICH8 },
136c1733db1SRobert Mustacchi { PCH_SMBUS_DH89xxCC, PCH_SMBUS_FEAT_ALL_ICH8 },
137c1733db1SRobert Mustacchi { PCH_SMBUS_DH89xxCL, PCH_SMBUS_FEAT_ALL_ICH8 },
138c1733db1SRobert Mustacchi { PCH_SMBUS_PCH6, PCH_SMBUS_FEAT_ALL_ICH8 },
139c1733db1SRobert Mustacchi { PCH_SMBUS_PCH7, PCH_SMBUS_FEAT_ALL_ICH8 },
140c1733db1SRobert Mustacchi { PCH_SMBUS_PCH8, PCH_SMBUS_FEAT_ALL_ICH8 },
141c1733db1SRobert Mustacchi { PCH_SMBUS_PCH8_LP, PCH_SMBUS_FEAT_ALL_ICH8 },
142c1733db1SRobert Mustacchi { PCH_SMBUS_C610, PCH_SMBUS_FEAT_ALL_ICH8 },
143c1733db1SRobert Mustacchi { PCH_SMBUS_C610_MS0, PCH_SMBUS_FEAT_ALL_ICH8 },
144c1733db1SRobert Mustacchi { PCH_SMBUS_C610_MS1, PCH_SMBUS_FEAT_ALL_ICH8 },
145c1733db1SRobert Mustacchi { PCH_SMBUS_C610_MS2, PCH_SMBUS_FEAT_ALL_ICH8 },
146c1733db1SRobert Mustacchi { PCH_SMBUS_PCH9, PCH_SMBUS_FEAT_ALL_ICH8 },
147c1733db1SRobert Mustacchi { PCH_SMBUS_PCH9_LP, PCH_SMBUS_FEAT_ALL_ICH8 },
148c1733db1SRobert Mustacchi { PCH_SMBUS_BAYTRAIL, PCH_SMBUS_FEAT_ALL_ICH8 },
149c1733db1SRobert Mustacchi { PCH_SMBUS_100, PCH_SMBUS_FEAT_ALL_ICH8 },
150c1733db1SRobert Mustacchi { PCH_SMBUS_DENVERTON, PCH_SMBUS_FEAT_ALL_ICH8 },
151c1733db1SRobert Mustacchi { PCH_SMBUS_C740, PCH_SMBUS_FEAT_ALL_ICH8 },
152c1733db1SRobert Mustacchi { PCH_SMBUS_APOLLO, PCH_SMBUS_FEAT_ALL_ICH8 },
153c1733db1SRobert Mustacchi { PCH_SMBUS_200, PCH_SMBUS_FEAT_ALL_ICH8 },
154c1733db1SRobert Mustacchi { PCH_SMBUS_GEMINI, PCH_SMBUS_FEAT_ALL_ICH8 },
155c1733db1SRobert Mustacchi { PCH_SMBUS_C620, PCH_SMBUS_FEAT_ALL_ICH8 },
156c1733db1SRobert Mustacchi { PCH_SMBUS_C620_SUPER, PCH_SMBUS_FEAT_ALL_ICH8 },
157c1733db1SRobert Mustacchi { PCH_SMBUS_300, PCH_SMBUS_FEAT_ALL_ICH8 },
158c1733db1SRobert Mustacchi { PCH_SMBUS_ICE_LAKE_D, PCH_SMBUS_FEAT_ALL_ICH8 },
159c1733db1SRobert Mustacchi { PCH_SMBUS_495_PKG, PCH_SMBUS_FEAT_ALL_ICH8 },
160c1733db1SRobert Mustacchi { PCH_SMBUS_400, PCH_SMBUS_FEAT_ALL_ICH8 },
161c1733db1SRobert Mustacchi { PCH_SMBUS_400_PKG, PCH_SMBUS_FEAT_ALL_ICH8 },
162c1733db1SRobert Mustacchi { PCH_SMBUS_ELKHART, PCH_SMBUS_FEAT_ALL_ICH8 },
163c1733db1SRobert Mustacchi { PCH_SMBUS_500, PCH_SMBUS_FEAT_ALL_ICH8 },
164c1733db1SRobert Mustacchi { PCH_SMBUS_500_PKG, PCH_SMBUS_FEAT_ALL_ICH8 },
165c1733db1SRobert Mustacchi { PCH_SMBUS_JASPER, PCH_SMBUS_FEAT_ALL_ICH8 },
166c1733db1SRobert Mustacchi { PCH_SMBUS_600, PCH_SMBUS_FEAT_ALL_ICH8 },
167c1733db1SRobert Mustacchi { PCH_SMBUS_600_PKG, PCH_SMBUS_FEAT_ALL_ICH8 },
168*9fe9c13cSRobert Mustacchi { PCH_SMBUS_ALDER_N, PCH_SMBUS_FEAT_ALL_ICH8 },
169*9fe9c13cSRobert Mustacchi { PCH_SMBUS_700, PCH_SMBUS_FEAT_ALL_ICH8 },
170c1733db1SRobert Mustacchi { PCH_SMBUS_800, PCH_SMBUS_FEAT_ALL_ICH8 },
171c1733db1SRobert Mustacchi { PCH_SMBUS_METEOR_PS, PCH_SMBUS_FEAT_ALL_ICH8 },
172c1733db1SRobert Mustacchi { PCH_SMBUS_PANTHER_H, PCH_SMBUS_FEAT_ALL_ICH8 },
173*9fe9c13cSRobert Mustacchi { PCH_SMBUS_PANTHER_P, PCH_SMBUS_FEAT_ALL_ICH8 },
174*9fe9c13cSRobert Mustacchi { PCH_SMBUS_900, PCH_SMBUS_FEAT_ALL_ICH8 }
175c1733db1SRobert Mustacchi };
176c1733db1SRobert Mustacchi
177c1733db1SRobert Mustacchi static uint8_t
pchsmbus_read8(pchsmbus_t * pch,uint8_t regno)178c1733db1SRobert Mustacchi pchsmbus_read8(pchsmbus_t *pch, uint8_t regno)
179c1733db1SRobert Mustacchi {
180c1733db1SRobert Mustacchi ASSERT3U(regno, <, pch->ps_regsize);
181c1733db1SRobert Mustacchi
182c1733db1SRobert Mustacchi return (ddi_get8(pch->ps_regs, (uint8_t *)(pch->ps_base + regno)));
183c1733db1SRobert Mustacchi }
184c1733db1SRobert Mustacchi
185c1733db1SRobert Mustacchi static void
pchsmbus_write8(pchsmbus_t * pch,uint8_t regno,uint8_t val)186c1733db1SRobert Mustacchi pchsmbus_write8(pchsmbus_t *pch, uint8_t regno, uint8_t val)
187c1733db1SRobert Mustacchi {
188c1733db1SRobert Mustacchi ASSERT3U(regno, <, pch->ps_regsize);
189c1733db1SRobert Mustacchi
190c1733db1SRobert Mustacchi ddi_put8(pch->ps_regs, (uint8_t *)(pch->ps_base + regno), val);
191c1733db1SRobert Mustacchi }
192c1733db1SRobert Mustacchi
193c1733db1SRobert Mustacchi static i2c_errno_t
pchsmbus_prop_info(void * arg,i2c_prop_t prop,i2c_prop_info_t * info)194c1733db1SRobert Mustacchi pchsmbus_prop_info(void *arg, i2c_prop_t prop, i2c_prop_info_t *info)
195c1733db1SRobert Mustacchi {
196c1733db1SRobert Mustacchi switch (prop) {
197c1733db1SRobert Mustacchi case I2C_PROP_BUS_SPEED:
198c1733db1SRobert Mustacchi i2c_prop_info_set_def_u32(info, I2C_SPEED_STD);
199c1733db1SRobert Mustacchi i2c_prop_info_set_pos_bit32(info, I2C_SPEED_STD);
200c1733db1SRobert Mustacchi break;
201c1733db1SRobert Mustacchi case SMBUS_PROP_SUP_OPS:
202c1733db1SRobert Mustacchi case SMBUS_PROP_MAX_BLOCK:
203c1733db1SRobert Mustacchi break;
204c1733db1SRobert Mustacchi default:
205c1733db1SRobert Mustacchi return (I2C_PROP_E_UNSUP);
206c1733db1SRobert Mustacchi }
207c1733db1SRobert Mustacchi
208c1733db1SRobert Mustacchi /*
209c1733db1SRobert Mustacchi * We can't set any timing properties or the speed really, so we
210c1733db1SRobert Mustacchi * indicate that all properties are read-only.
211c1733db1SRobert Mustacchi */
212c1733db1SRobert Mustacchi i2c_prop_info_set_perm(info, I2C_PROP_PERM_RO);
213c1733db1SRobert Mustacchi
214c1733db1SRobert Mustacchi return (I2C_CORE_E_OK);
215c1733db1SRobert Mustacchi }
216c1733db1SRobert Mustacchi
217c1733db1SRobert Mustacchi static i2c_errno_t
pchsmbus_prop_get(void * arg,i2c_prop_t prop,void * buf,size_t buflen)218c1733db1SRobert Mustacchi pchsmbus_prop_get(void *arg, i2c_prop_t prop, void *buf, size_t buflen)
219c1733db1SRobert Mustacchi {
220c1733db1SRobert Mustacchi uint32_t val;
221c1733db1SRobert Mustacchi
222c1733db1SRobert Mustacchi switch (prop) {
223c1733db1SRobert Mustacchi case I2C_PROP_BUS_SPEED:
224c1733db1SRobert Mustacchi val = I2C_SPEED_STD;
225c1733db1SRobert Mustacchi break;
226c1733db1SRobert Mustacchi case SMBUS_PROP_SUP_OPS:
227c1733db1SRobert Mustacchi val = SMBUS_PROP_OP_QUICK_COMMAND | SMBUS_PROP_OP_SEND_BYTE |
228c1733db1SRobert Mustacchi SMBUS_PROP_OP_RECV_BYTE | SMBUS_PROP_OP_WRITE_BYTE |
229c1733db1SRobert Mustacchi SMBUS_PROP_OP_READ_BYTE | SMBUS_PROP_OP_WRITE_WORD |
230c1733db1SRobert Mustacchi SMBUS_PROP_OP_READ_WORD | SMBUS_PROP_OP_PROCESS_CALL |
231c1733db1SRobert Mustacchi SMBUS_PROP_OP_WRITE_BLOCK | SMBUS_PROP_OP_READ_BLOCK |
232c1733db1SRobert Mustacchi SMBUS_PROP_OP_BLOCK_PROCESS_CALL |
233c1733db1SRobert Mustacchi SMBUS_PROP_OP_I2C_WRITE_BLOCK |
234c1733db1SRobert Mustacchi SMBUS_PROP_OP_I2C_READ_BLOCK;
235c1733db1SRobert Mustacchi break;
236c1733db1SRobert Mustacchi case SMBUS_PROP_MAX_BLOCK:
237c1733db1SRobert Mustacchi val = SMBUS_V2_MAX_BLOCK;
238c1733db1SRobert Mustacchi break;
239c1733db1SRobert Mustacchi default:
240c1733db1SRobert Mustacchi return (I2C_PROP_E_UNSUP);
241c1733db1SRobert Mustacchi }
242c1733db1SRobert Mustacchi
243c1733db1SRobert Mustacchi VERIFY3U(buflen, >=, sizeof (val));
244c1733db1SRobert Mustacchi bcopy(&val, buf, sizeof (val));
245c1733db1SRobert Mustacchi return (I2C_CORE_E_OK);
246c1733db1SRobert Mustacchi }
247c1733db1SRobert Mustacchi
248c1733db1SRobert Mustacchi static bool
pchsmbus_bus_avail(pchsmbus_t * pch)249c1733db1SRobert Mustacchi pchsmbus_bus_avail(pchsmbus_t *pch)
250c1733db1SRobert Mustacchi {
251c1733db1SRobert Mustacchi const uint32_t count = i2c_ctrl_timeout_count(pch->ps_hdl,
252c1733db1SRobert Mustacchi I2C_CTRL_TO_BUS_ACT);
253c1733db1SRobert Mustacchi const uint32_t wait = i2c_ctrl_timeout_delay_us(pch->ps_hdl,
254c1733db1SRobert Mustacchi I2C_CTRL_TO_BUS_ACT);
255c1733db1SRobert Mustacchi
256c1733db1SRobert Mustacchi for (uint32_t i = 0; i < count; i++) {
257c1733db1SRobert Mustacchi uint8_t hsts = pchsmbus_read8(pch, PCH_R_BAR_HSTS);
258c1733db1SRobert Mustacchi if ((hsts & PCH_HSTS_BUSY) == 0) {
259c1733db1SRobert Mustacchi return (true);
260c1733db1SRobert Mustacchi }
261c1733db1SRobert Mustacchi
262c1733db1SRobert Mustacchi delay(drv_usectohz(wait));
263c1733db1SRobert Mustacchi }
264c1733db1SRobert Mustacchi
265c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "controller timed out waiting for "
266c1733db1SRobert Mustacchi "bus activity to cease");
267c1733db1SRobert Mustacchi return (false);
268c1733db1SRobert Mustacchi }
269c1733db1SRobert Mustacchi
270c1733db1SRobert Mustacchi static void
pchsmbus_set_addr(pchsmbus_t * pch,const smbus_req_t * req,bool write)271c1733db1SRobert Mustacchi pchsmbus_set_addr(pchsmbus_t *pch, const smbus_req_t *req, bool write)
272c1733db1SRobert Mustacchi {
273c1733db1SRobert Mustacchi uint8_t addr = 0;
274c1733db1SRobert Mustacchi uint8_t wbit = write ? PCH_R_TSA_RW_WRITE : PCH_R_TSA_RW_READ;
275c1733db1SRobert Mustacchi
276c1733db1SRobert Mustacchi
277c1733db1SRobert Mustacchi ASSERT3U(req->smbr_addr.ia_type, ==, I2C_ADDR_7BIT);
278c1733db1SRobert Mustacchi addr = PCH_R_TSA_SET_ADDR(addr, req->smbr_addr.ia_addr);
279c1733db1SRobert Mustacchi addr = PCH_R_TSA_SET_RW(addr, wbit);
280c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_TSA, addr);
281c1733db1SRobert Mustacchi }
282c1733db1SRobert Mustacchi
283c1733db1SRobert Mustacchi static pch_smbus_cmd_t
pchsmbus_req_to_cmd(const smbus_req_t * req)284c1733db1SRobert Mustacchi pchsmbus_req_to_cmd(const smbus_req_t *req)
285c1733db1SRobert Mustacchi {
286c1733db1SRobert Mustacchi switch (req->smbr_op) {
287c1733db1SRobert Mustacchi case SMBUS_OP_QUICK_COMMAND:
288c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_QUICK);
289c1733db1SRobert Mustacchi case SMBUS_OP_SEND_BYTE:
290c1733db1SRobert Mustacchi case SMBUS_OP_RECV_BYTE:
291c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_BYTE);
292c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BYTE:
293c1733db1SRobert Mustacchi case SMBUS_OP_READ_BYTE:
294c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_BYTE_DATA);
295c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_WORD:
296c1733db1SRobert Mustacchi case SMBUS_OP_READ_WORD:
297c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_WORD_DATA);
298c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BLOCK:
299c1733db1SRobert Mustacchi case SMBUS_OP_READ_BLOCK:
300c1733db1SRobert Mustacchi case SMBUS_OP_I2C_WRITE_BLOCK:
301c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_BLOCK);
302c1733db1SRobert Mustacchi case SMBUS_OP_PROCESS_CALL:
303c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_PROC_CALL);
304c1733db1SRobert Mustacchi case SMBUS_OP_BLOCK_PROCESS_CALL:
305c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_BLOCK_PROC);
306c1733db1SRobert Mustacchi case SMBUS_OP_I2C_READ_BLOCK:
307c1733db1SRobert Mustacchi return (PCH_SMBUS_CMD_I2C_READ);
308c1733db1SRobert Mustacchi default:
309c1733db1SRobert Mustacchi panic("asked to translate unexpected request type: 0x%x",
310c1733db1SRobert Mustacchi req->smbr_op);
311c1733db1SRobert Mustacchi }
312c1733db1SRobert Mustacchi }
313c1733db1SRobert Mustacchi
314c1733db1SRobert Mustacchi /*
315c1733db1SRobert Mustacchi * Initialize a block request. These are the most complicated requests to deal
316c1733db1SRobert Mustacchi * with because of the different variations. There are two modes of operation: a
317c1733db1SRobert Mustacchi * 32 byte buffer that we can use to just take care of the operation in one shot
318c1733db1SRobert Mustacchi * or a single byte at a time operation. Most hardware supports the 32 byte
319c1733db1SRobert Mustacchi * buffer; however, when performing an I2C read or write, it must operate in
320c1733db1SRobert Mustacchi * byte at a time mode.
321c1733db1SRobert Mustacchi *
322c1733db1SRobert Mustacchi * We also need to update the controller registers. This means specifically:
323c1733db1SRobert Mustacchi *
324c1733db1SRobert Mustacchi * - Enabling I2C mode specifically for I2C block writes. This is not required
325c1733db1SRobert Mustacchi * for I2C block reads which have their own dedicated command in the
326c1733db1SRobert Mustacchi * controller.
327c1733db1SRobert Mustacchi * - Enabling the 32-byte buffer for block reads when it's supported in
328c1733db1SRobert Mustacchi * hardware. This cannot be done for I2C operations and must be done for
329c1733db1SRobert Mustacchi * block procedure calls. We will not advertise support for block procedure
330c1733db1SRobert Mustacchi * calls to the framework if they are not supported in hardware.
331c1733db1SRobert Mustacchi *
332c1733db1SRobert Mustacchi * Note, regardless of whether this is the 'i2c' form or not, we are going to
333c1733db1SRobert Mustacchi * end up issuing the 'command' register. When doing an i2c block read, the
334c1733db1SRobert Mustacchi * controller will issue a repeated start and do the transition to a read.
335c1733db1SRobert Mustacchi */
336c1733db1SRobert Mustacchi static void
pchsmbus_io_init_block(pchsmbus_t * pch,const smbus_req_t * req)337c1733db1SRobert Mustacchi pchsmbus_io_init_block(pchsmbus_t *pch, const smbus_req_t *req)
338c1733db1SRobert Mustacchi {
339c1733db1SRobert Mustacchi bool write, want_buf = false, want_i2c = false;
340c1733db1SRobert Mustacchi
341c1733db1SRobert Mustacchi switch (req->smbr_op) {
342c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BLOCK:
343c1733db1SRobert Mustacchi case SMBUS_OP_BLOCK_PROCESS_CALL:
344c1733db1SRobert Mustacchi want_buf = true;
345c1733db1SRobert Mustacchi write = true;
346c1733db1SRobert Mustacchi break;
347c1733db1SRobert Mustacchi case SMBUS_OP_READ_BLOCK:
348c1733db1SRobert Mustacchi want_buf = true;
349c1733db1SRobert Mustacchi write = false;
350c1733db1SRobert Mustacchi break;
351c1733db1SRobert Mustacchi case SMBUS_OP_I2C_WRITE_BLOCK:
352c1733db1SRobert Mustacchi write = true;
353c1733db1SRobert Mustacchi want_buf = false;
354c1733db1SRobert Mustacchi /*
355c1733db1SRobert Mustacchi * This is the only operation that requires an explicit I2C
356c1733db1SRobert Mustacchi * enable. This causes us to skip sending the byte count. This
357c1733db1SRobert Mustacchi * isn't required for the I2C Read Block operation because it's
358c1733db1SRobert Mustacchi * just part of the controller's semantics.
359c1733db1SRobert Mustacchi */
360c1733db1SRobert Mustacchi want_i2c = true;
361c1733db1SRobert Mustacchi break;
362c1733db1SRobert Mustacchi case SMBUS_OP_I2C_READ_BLOCK:
363c1733db1SRobert Mustacchi /*
364c1733db1SRobert Mustacchi * Yes, this seems on the face an oxymoron. The reason for this
365c1733db1SRobert Mustacchi * is buried in the datasheets (though some are inconsistent).
366c1733db1SRobert Mustacchi * When issuing an I2C block read we first are going to do a
367c1733db1SRobert Mustacchi * write with a byte and then issue a repeated start with a
368c1733db1SRobert Mustacchi * read. The first thing we do will be a write, hence we set
369c1733db1SRobert Mustacchi * this.
370c1733db1SRobert Mustacchi *
371c1733db1SRobert Mustacchi * However, this gets more nuanced. There exists the SPD write
372c1733db1SRobert Mustacchi * disable bit which was added in the PCH7 generation. When this
373c1733db1SRobert Mustacchi * is set, this needs to be false. This is likely why some
374c1733db1SRobert Mustacchi * chipsets after this generation say this should always be
375c1733db1SRobert Mustacchi * treated as a read (i.e. Ice Lake-D); however, this is also
376c1733db1SRobert Mustacchi * contradicted by other devices between PCH7 and Ice Lake such
377c1733db1SRobert Mustacchi * as the 100/200-series chipsets. A right mess, isn't it?
378c1733db1SRobert Mustacchi */
379c1733db1SRobert Mustacchi write = PCH_R_HCFG_GET_SPDWD(pch->ps_init_hcfg) == 0;
380c1733db1SRobert Mustacchi break;
381c1733db1SRobert Mustacchi default:
382c1733db1SRobert Mustacchi panic("programmer error: not a block type: 0x%x\n",
383c1733db1SRobert Mustacchi req->smbr_op);
384c1733db1SRobert Mustacchi }
385c1733db1SRobert Mustacchi
386c1733db1SRobert Mustacchi if ((pch->ps_feats & PCH_SMBUS_FEAT_32B_BUF) == 0)
387c1733db1SRobert Mustacchi want_buf = false;
388c1733db1SRobert Mustacchi
389c1733db1SRobert Mustacchi VERIFY(!(want_i2c && want_buf));
390c1733db1SRobert Mustacchi if (want_i2c) {
391c1733db1SRobert Mustacchi uint32_t val = pci_config_get32(pch->ps_cfg, PCH_R_PCIE_HCFG);
392c1733db1SRobert Mustacchi val = PCH_R_HCFG_SET_I2CEN(val, 1);
393c1733db1SRobert Mustacchi pci_config_put32(pch->ps_cfg, PCH_R_PCIE_HCFG, val);
394c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_RUN_I2C_EN;
395c1733db1SRobert Mustacchi }
396c1733db1SRobert Mustacchi
397c1733db1SRobert Mustacchi if (want_buf) {
398c1733db1SRobert Mustacchi uint8_t val = pchsmbus_read8(pch, PCH_R_BAR_AUXC);
399c1733db1SRobert Mustacchi val = PCH_R_AUXC_SET_E32B(val, 1);
400c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_AUXC, val);
401c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_RUN_BUF_EN;
402c1733db1SRobert Mustacchi }
403c1733db1SRobert Mustacchi
404c1733db1SRobert Mustacchi /*
405c1733db1SRobert Mustacchi * All operations get the address and the command register set. Though
406c1733db1SRobert Mustacchi * of course the I2C Block read actually doesn't use the command
407c1733db1SRobert Mustacchi * register and instead uses the data 1 register for it.
408c1733db1SRobert Mustacchi */
409c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, write);
410c1733db1SRobert Mustacchi if (req->smbr_op == SMBUS_OP_I2C_READ_BLOCK) {
411c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HD1, req->smbr_cmd);
412c1733db1SRobert Mustacchi } else {
413c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCMD, req->smbr_cmd);
414c1733db1SRobert Mustacchi }
415c1733db1SRobert Mustacchi
416c1733db1SRobert Mustacchi /*
417c1733db1SRobert Mustacchi * If this is a read command, there is nothing else to do. For the
418c1733db1SRobert Mustacchi * various write types we must actually write the data in question.
419c1733db1SRobert Mustacchi */
420c1733db1SRobert Mustacchi if (req->smbr_op == SMBUS_OP_I2C_READ_BLOCK || SMBUS_OP_READ_BLOCK) {
421c1733db1SRobert Mustacchi return;
422c1733db1SRobert Mustacchi }
423c1733db1SRobert Mustacchi
424c1733db1SRobert Mustacchi /*
425c1733db1SRobert Mustacchi * For all writes, regardless of length, indicate how many bytes are in
426c1733db1SRobert Mustacchi * the transaction.
427c1733db1SRobert Mustacchi */
428c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HD0, req->smbr_wlen);
429c1733db1SRobert Mustacchi uint16_t wlen = req->smbr_wlen;
430c1733db1SRobert Mustacchi if (!want_buf) {
431c1733db1SRobert Mustacchi wlen = 1;
432c1733db1SRobert Mustacchi }
433c1733db1SRobert Mustacchi
434c1733db1SRobert Mustacchi /*
435c1733db1SRobert Mustacchi * Explicitly reset the index into the buffer. This is a nop if we're
436c1733db1SRobert Mustacchi * not using the buffer.
437c1733db1SRobert Mustacchi */
438c1733db1SRobert Mustacchi (void) pchsmbus_read8(pch, PCH_R_BAR_HCTL);
439c1733db1SRobert Mustacchi for (uint16_t i = 0; i < wlen; i++, pch->ps_req_off++) {
440c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HBD, req->smbr_wdata[i]);
441c1733db1SRobert Mustacchi }
442c1733db1SRobert Mustacchi }
443c1733db1SRobert Mustacchi
444c1733db1SRobert Mustacchi /*
445c1733db1SRobert Mustacchi * We have one of three different general classes of errors that we need to
446c1733db1SRobert Mustacchi * prioritize and synthesize into useful errors upstack. We treat them in the
447c1733db1SRobert Mustacchi * following order:
448c1733db1SRobert Mustacchi *
449c1733db1SRobert Mustacchi * 1) The FAIL error takes priority as this is set due to a request by us to
450c1733db1SRobert Mustacchi * abort the error. The driver sets the appropriate error to use in our
451c1733db1SRobert Mustacchi * device structure before issuing this.
452c1733db1SRobert Mustacchi * 2) The Bus Error indicates that something went wrong on the bus itself. The
453c1733db1SRobert Mustacchi * datasheet says it's a general transaction collision or a bus arbitration
454c1733db1SRobert Mustacchi * loss. We always translate that into I2C_CTRL_E_ARB_LOST.
455c1733db1SRobert Mustacchi * 3) The device error is a combination of different possibilities. The most
456c1733db1SRobert Mustacchi * common case is getting no acknowledgement. However, this can also happen
457c1733db1SRobert Mustacchi * because the driver requests an illegal command, a PEC error occurs, or we
458c1733db1SRobert Mustacchi * exceed the 25 ms SMBus timeout. This is definitely unfortunate, but we
459c1733db1SRobert Mustacchi * basically just stick to the unknown I2C_CTRL_E_NACK.
460c1733db1SRobert Mustacchi */
461c1733db1SRobert Mustacchi static void
pchsmbus_io_error(pchsmbus_t * pch,pch_smbus_sts_t status)462c1733db1SRobert Mustacchi pchsmbus_io_error(pchsmbus_t *pch, pch_smbus_sts_t status)
463c1733db1SRobert Mustacchi {
464c1733db1SRobert Mustacchi i2c_ctrl_error_t err;
465c1733db1SRobert Mustacchi
466c1733db1SRobert Mustacchi if ((status & PCH_HSTS_FAIL) != 0) {
467c1733db1SRobert Mustacchi ASSERT3U(pch->ps_kill_err, !=, I2C_CTRL_E_OK);
468c1733db1SRobert Mustacchi err = pch->ps_kill_err;
469c1733db1SRobert Mustacchi } else if ((status & PCH_HSTS_BUS_ERR)) {
470c1733db1SRobert Mustacchi err = I2C_CTRL_E_ARB_LOST;
471c1733db1SRobert Mustacchi } else {
472c1733db1SRobert Mustacchi err = I2C_CTRL_E_NACK;
473c1733db1SRobert Mustacchi }
474c1733db1SRobert Mustacchi
475c1733db1SRobert Mustacchi i2c_ctrl_io_error(&pch->ps_req->smbr_error, I2C_CORE_E_CONTROLLER,
476c1733db1SRobert Mustacchi err);
477c1733db1SRobert Mustacchi pch->ps_req_done = true;
478c1733db1SRobert Mustacchi }
479c1733db1SRobert Mustacchi
480c1733db1SRobert Mustacchi /*
481c1733db1SRobert Mustacchi * We have received a byte done callback. This means that we're either
482c1733db1SRobert Mustacchi * performing a read or write. The controller does not support performing both
483c1733db1SRobert Mustacchi * without the buffer enabled.
484c1733db1SRobert Mustacchi *
485c1733db1SRobert Mustacchi * If we are writing, we need to write the next byte into the buffer. If there
486c1733db1SRobert Mustacchi * is any more.
487c1733db1SRobert Mustacchi *
488c1733db1SRobert Mustacchi * If we are reading, we need to read the next byte out of the buffer. It the
489c1733db1SRobert Mustacchi * subsequent byte (the one after we just read) would be the last one, then we
490c1733db1SRobert Mustacchi * need to indicate to the controller that this it will be the last byte. When
491c1733db1SRobert Mustacchi * executing an SMBus block read, the data length is not known in advance.
492c1733db1SRobert Mustacchi *
493c1733db1SRobert Mustacchi * In both cases, reading or writing all bytes is not indicating of completing
494c1733db1SRobert Mustacchi * the command. The controller explicitly sets the INTR status bit for that.
495c1733db1SRobert Mustacchi */
496c1733db1SRobert Mustacchi static void
pchsmbus_io_byte_done(pchsmbus_t * pch)497c1733db1SRobert Mustacchi pchsmbus_io_byte_done(pchsmbus_t *pch)
498c1733db1SRobert Mustacchi {
499c1733db1SRobert Mustacchi ASSERT(MUTEX_HELD(&pch->ps_mutex));
500c1733db1SRobert Mustacchi ASSERT3U(pch->ps_init & PCHSMBUS_RUN_BUF_EN, ==, 0);
501c1733db1SRobert Mustacchi ASSERT3P(pch->ps_req, !=, NULL);
502c1733db1SRobert Mustacchi
503c1733db1SRobert Mustacchi if (pch->ps_req->smbr_op == SMBUS_OP_WRITE_BLOCK ||
504c1733db1SRobert Mustacchi pch->ps_req->smbr_op == SMBUS_OP_I2C_WRITE_BLOCK) {
505c1733db1SRobert Mustacchi if (pch->ps_req_off < pch->ps_req->smbr_wlen) {
506c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HBD,
507c1733db1SRobert Mustacchi pch->ps_req->smbr_wdata[pch->ps_req_off]);
508c1733db1SRobert Mustacchi pch->ps_req_off++;
509c1733db1SRobert Mustacchi }
510c1733db1SRobert Mustacchi return;
511c1733db1SRobert Mustacchi }
512c1733db1SRobert Mustacchi
513c1733db1SRobert Mustacchi /*
514c1733db1SRobert Mustacchi * I2C block reads already know the size that they care about. However,
515c1733db1SRobert Mustacchi * normal SMBus block reads have it in their first byte, which will be
516c1733db1SRobert Mustacchi * in the HD0 register, not the HDB register like normal data.
517c1733db1SRobert Mustacchi */
518c1733db1SRobert Mustacchi if (pch->ps_req->smbr_rlen == 0) {
519c1733db1SRobert Mustacchi ASSERT3U(pch->ps_req->smbr_op, ==, SMBUS_OP_READ_BLOCK);
520c1733db1SRobert Mustacchi
521c1733db1SRobert Mustacchi uint8_t len = pchsmbus_read8(pch, PCH_R_BAR_HD0);
522c1733db1SRobert Mustacchi if (len == 0 || len > SMBUS_V2_MAX_BLOCK) {
523c1733db1SRobert Mustacchi pch->ps_kill_err = I2C_CTRL_E_BAD_SMBUS_RLEN;
524c1733db1SRobert Mustacchi uint8_t val = PCH_R_HCTL_SET_KILL(0, 1);
525c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCTL, val);
526c1733db1SRobert Mustacchi return;
527c1733db1SRobert Mustacchi }
528c1733db1SRobert Mustacchi pch->ps_req->smbr_rlen = len;
529c1733db1SRobert Mustacchi return;
530c1733db1SRobert Mustacchi }
531c1733db1SRobert Mustacchi
532c1733db1SRobert Mustacchi pch->ps_req->smbr_rdata[pch->ps_req_off] = pchsmbus_read8(pch,
533c1733db1SRobert Mustacchi PCH_R_BAR_HBD);
534c1733db1SRobert Mustacchi pch->ps_req_off++;
535c1733db1SRobert Mustacchi if (pch->ps_req_off + 1 == pch->ps_req->smbr_rlen) {
536c1733db1SRobert Mustacchi uint8_t hctl = PCH_R_HCTL_SET_LAST(pch->ps_req_hctl, 1);
537c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCTL, hctl);
538c1733db1SRobert Mustacchi }
539c1733db1SRobert Mustacchi }
540c1733db1SRobert Mustacchi
541c1733db1SRobert Mustacchi /*
542c1733db1SRobert Mustacchi * We've been told that the request completed successfully. The action that we
543c1733db1SRobert Mustacchi * must take will vary based upon the type of request. Here is where we read out
544c1733db1SRobert Mustacchi * result data. For writes, we're simply done. Note, for block requests, we will
545c1733db1SRobert Mustacchi * have already processed it if we're not operating in block mode.
546c1733db1SRobert Mustacchi */
547c1733db1SRobert Mustacchi static void
pchsmbus_io_req_done(pchsmbus_t * pch)548c1733db1SRobert Mustacchi pchsmbus_io_req_done(pchsmbus_t *pch)
549c1733db1SRobert Mustacchi {
550c1733db1SRobert Mustacchi uint8_t len;
551c1733db1SRobert Mustacchi
552c1733db1SRobert Mustacchi pch->ps_req_done = true;
553c1733db1SRobert Mustacchi switch (pch->ps_req->smbr_op) {
554c1733db1SRobert Mustacchi case SMBUS_OP_QUICK_COMMAND:
555c1733db1SRobert Mustacchi case SMBUS_OP_SEND_BYTE:
556c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BYTE:
557c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_WORD:
558c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BLOCK:
559c1733db1SRobert Mustacchi case SMBUS_OP_I2C_WRITE_BLOCK:
560c1733db1SRobert Mustacchi /*
561c1733db1SRobert Mustacchi * There is nothing to do for all write requests.
562c1733db1SRobert Mustacchi */
563c1733db1SRobert Mustacchi break;
564c1733db1SRobert Mustacchi case SMBUS_OP_RECV_BYTE:
565c1733db1SRobert Mustacchi case SMBUS_OP_READ_BYTE:
566c1733db1SRobert Mustacchi pch->ps_req->smbr_rdata[0] = pchsmbus_read8(pch, PCH_R_BAR_HD0);
567c1733db1SRobert Mustacchi break;
568c1733db1SRobert Mustacchi case SMBUS_OP_READ_WORD:
569c1733db1SRobert Mustacchi case SMBUS_OP_PROCESS_CALL:
570c1733db1SRobert Mustacchi pch->ps_req->smbr_rdata[0] = pchsmbus_read8(pch, PCH_R_BAR_HD0);
571c1733db1SRobert Mustacchi pch->ps_req->smbr_rdata[1] = pchsmbus_read8(pch, PCH_R_BAR_HD1);
572c1733db1SRobert Mustacchi break;
573c1733db1SRobert Mustacchi case SMBUS_OP_READ_BLOCK:
574c1733db1SRobert Mustacchi case SMBUS_OP_BLOCK_PROCESS_CALL:
575c1733db1SRobert Mustacchi case SMBUS_OP_I2C_READ_BLOCK:
576c1733db1SRobert Mustacchi /*
577c1733db1SRobert Mustacchi * Byte mode already has all of its data.
578c1733db1SRobert Mustacchi */
579c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_RUN_BUF_EN) == 0) {
580c1733db1SRobert Mustacchi break;
581c1733db1SRobert Mustacchi }
582c1733db1SRobert Mustacchi
583c1733db1SRobert Mustacchi len = pchsmbus_read8(pch, PCH_R_BAR_HD0);
584c1733db1SRobert Mustacchi if (len == 0 || len > SMBUS_V2_MAX_BLOCK) {
585c1733db1SRobert Mustacchi i2c_ctrl_io_error(&pch->ps_req->smbr_error,
586c1733db1SRobert Mustacchi I2C_CORE_E_CONTROLLER,
587c1733db1SRobert Mustacchi I2C_CTRL_E_BAD_SMBUS_RLEN);
588c1733db1SRobert Mustacchi return;
589c1733db1SRobert Mustacchi }
590c1733db1SRobert Mustacchi
591c1733db1SRobert Mustacchi pch->ps_req->smbr_rlen = len;
592c1733db1SRobert Mustacchi /* Explicitly reset the buffer index */
593c1733db1SRobert Mustacchi (void) pchsmbus_read8(pch, PCH_R_BAR_HCTL);
594c1733db1SRobert Mustacchi for (uint16_t i = 0; i < pch->ps_req->smbr_rlen; i++) {
595c1733db1SRobert Mustacchi pch->ps_req->smbr_rdata[i] = pchsmbus_read8(pch,
596c1733db1SRobert Mustacchi PCH_R_BAR_HBD);
597c1733db1SRobert Mustacchi }
598c1733db1SRobert Mustacchi break;
599c1733db1SRobert Mustacchi case SMBUS_OP_HOST_NOTIFY:
600c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U32:
601c1733db1SRobert Mustacchi case SMBUS_OP_READ_U32:
602c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U64:
603c1733db1SRobert Mustacchi case SMBUS_OP_READ_U64:
604c1733db1SRobert Mustacchi default:
605c1733db1SRobert Mustacchi panic("programmer error: unsupported request type 0x%x should "
606c1733db1SRobert Mustacchi "not have been completed", pch->ps_req->smbr_op);
607c1733db1SRobert Mustacchi }
608c1733db1SRobert Mustacchi
609c1733db1SRobert Mustacchi i2c_ctrl_io_success(&pch->ps_req->smbr_error);
610c1733db1SRobert Mustacchi }
611c1733db1SRobert Mustacchi
612c1733db1SRobert Mustacchi /*
613c1733db1SRobert Mustacchi * We have been given a status register read from the driver, whether by polling
614c1733db1SRobert Mustacchi * or by an interrupt. We must look at the bits present, clear anything that
615c1733db1SRobert Mustacchi * needs to be, and then take action to advance the state machine. Here's how we
616c1733db1SRobert Mustacchi * have to react to each bit:
617c1733db1SRobert Mustacchi *
618c1733db1SRobert Mustacchi * - Byte Done: This indicates a byte has been transferred when we're not in
619c1733db1SRobert Mustacchi * the 32 byte buffer mode. At this time, we either write the next byte or
620c1733db1SRobert Mustacchi * read the next byte out of the buffer.
621c1733db1SRobert Mustacchi * - Alert: This shouldn't be generated, so we generally ignore it, but clear
622c1733db1SRobert Mustacchi * it just for completeness.
623c1733db1SRobert Mustacchi * - Fail, Bus Error, Device Error: The transaction is over. We need to guess
624c1733db1SRobert Mustacchi * the best error that we can with the unfortunately limited information that
625c1733db1SRobert Mustacchi * we get.
626c1733db1SRobert Mustacchi * - Interrupt: This indicates that the entire command was completed and is the
627c1733db1SRobert Mustacchi * only thing that we should use to signal successful completion.
628c1733db1SRobert Mustacchi */
629c1733db1SRobert Mustacchi static bool
pchsmbus_io(pchsmbus_t * pch,pch_smbus_sts_t status)630c1733db1SRobert Mustacchi pchsmbus_io(pchsmbus_t *pch, pch_smbus_sts_t status)
631c1733db1SRobert Mustacchi {
632c1733db1SRobert Mustacchi ASSERT(MUTEX_HELD(&pch->ps_mutex));
633c1733db1SRobert Mustacchi
634c1733db1SRobert Mustacchi /*
635c1733db1SRobert Mustacchi * Is there actually activity for us to process or not. If not, then
636c1733db1SRobert Mustacchi * we're done. Mask off bits like In Use and related. Clear them now and
637c1733db1SRobert Mustacchi * proceed to process them all in turn.
638c1733db1SRobert Mustacchi */
639c1733db1SRobert Mustacchi status &= PCH_HSTS_CLEAR_PRE;
640c1733db1SRobert Mustacchi if (status == 0) {
641c1733db1SRobert Mustacchi return (false);
642c1733db1SRobert Mustacchi }
643c1733db1SRobert Mustacchi
644c1733db1SRobert Mustacchi if ((status & PCH_HSTS_ERRORS) != 0) {
645c1733db1SRobert Mustacchi pchsmbus_io_error(pch, status);
646c1733db1SRobert Mustacchi goto done;
647c1733db1SRobert Mustacchi }
648c1733db1SRobert Mustacchi
649c1733db1SRobert Mustacchi if ((status & PCH_HSTS_BYTE_DONE) != 0) {
650c1733db1SRobert Mustacchi pchsmbus_io_byte_done(pch);
651c1733db1SRobert Mustacchi }
652c1733db1SRobert Mustacchi
653c1733db1SRobert Mustacchi if ((status & PCH_HSTS_INTR) != 0) {
654c1733db1SRobert Mustacchi pchsmbus_io_req_done(pch);
655c1733db1SRobert Mustacchi }
656c1733db1SRobert Mustacchi
657c1733db1SRobert Mustacchi done:
658c1733db1SRobert Mustacchi /*
659c1733db1SRobert Mustacchi * We clear the status codes last as when operating in byte at a time
660c1733db1SRobert Mustacchi * mode, the data must be read and written prior to clearing this status
661c1733db1SRobert Mustacchi * to indicate that we are done.
662c1733db1SRobert Mustacchi */
663c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HSTS, status);
664c1733db1SRobert Mustacchi return (true);
665c1733db1SRobert Mustacchi }
666c1733db1SRobert Mustacchi
667c1733db1SRobert Mustacchi static uint_t
pchsmbus_intr(caddr_t arg1,caddr_t arg2)668c1733db1SRobert Mustacchi pchsmbus_intr(caddr_t arg1, caddr_t arg2)
669c1733db1SRobert Mustacchi {
670c1733db1SRobert Mustacchi pchsmbus_t *pch = (pchsmbus_t *)arg1;
671c1733db1SRobert Mustacchi pch_smbus_sts_t sts;
672c1733db1SRobert Mustacchi
673c1733db1SRobert Mustacchi mutex_enter(&pch->ps_mutex);
674c1733db1SRobert Mustacchi sts = pchsmbus_read8(pch, PCH_R_BAR_HSTS);
675c1733db1SRobert Mustacchi if (!pchsmbus_io(pch, sts)) {
676c1733db1SRobert Mustacchi mutex_exit(&pch->ps_mutex);
677c1733db1SRobert Mustacchi return (DDI_INTR_UNCLAIMED);
678c1733db1SRobert Mustacchi }
679c1733db1SRobert Mustacchi
680c1733db1SRobert Mustacchi if (pch->ps_req_done) {
681c1733db1SRobert Mustacchi cv_signal(&pch->ps_cv);
682c1733db1SRobert Mustacchi }
683c1733db1SRobert Mustacchi mutex_exit(&pch->ps_mutex);
684c1733db1SRobert Mustacchi return (DDI_INTR_CLAIMED);
685c1733db1SRobert Mustacchi }
686c1733db1SRobert Mustacchi
687c1733db1SRobert Mustacchi static void
pchsmbus_wait(pchsmbus_t * pch,bool poll)688c1733db1SRobert Mustacchi pchsmbus_wait(pchsmbus_t *pch, bool poll)
689c1733db1SRobert Mustacchi {
690c1733db1SRobert Mustacchi uint32_t to, spin;
691c1733db1SRobert Mustacchi
692c1733db1SRobert Mustacchi VERIFY(MUTEX_HELD(&pch->ps_mutex));
693c1733db1SRobert Mustacchi VERIFY3P(pch->ps_req, !=, NULL);
694c1733db1SRobert Mustacchi
695c1733db1SRobert Mustacchi to = i2c_ctrl_timeout_delay_us(pch->ps_hdl, I2C_CTRL_TO_IO);
696c1733db1SRobert Mustacchi spin = i2c_ctrl_timeout_delay_us(pch->ps_hdl, I2C_CTRL_TO_POLL_CTRL);
697c1733db1SRobert Mustacchi
698c1733db1SRobert Mustacchi if (!poll) {
699c1733db1SRobert Mustacchi clock_t abs = ddi_get_lbolt() + drv_usectohz(to);
700c1733db1SRobert Mustacchi while (!pch->ps_req_done) {
701c1733db1SRobert Mustacchi clock_t ret = cv_timedwait(&pch->ps_cv, &pch->ps_mutex,
702c1733db1SRobert Mustacchi abs);
703c1733db1SRobert Mustacchi if (ret == -1) {
704c1733db1SRobert Mustacchi break;
705c1733db1SRobert Mustacchi }
706c1733db1SRobert Mustacchi }
707c1733db1SRobert Mustacchi } else {
708c1733db1SRobert Mustacchi hrtime_t abs = gethrtime() + USEC2NSEC(to);
709c1733db1SRobert Mustacchi
710c1733db1SRobert Mustacchi while (!pch->ps_req_done && gethrtime() < abs) {
711c1733db1SRobert Mustacchi drv_usecwait(spin);
712c1733db1SRobert Mustacchi uint8_t status = pchsmbus_read8(pch, PCH_R_BAR_HSTS);
713c1733db1SRobert Mustacchi (void) pchsmbus_io(pch, status);
714c1733db1SRobert Mustacchi }
715c1733db1SRobert Mustacchi }
716c1733db1SRobert Mustacchi
717c1733db1SRobert Mustacchi /*
718c1733db1SRobert Mustacchi * If this is not done, we're going to set the kill bit. The next user
719c1733db1SRobert Mustacchi * will be the one that waits for the kill to actually complete with the
720c1733db1SRobert Mustacchi * normal call to pchsmbus_bus_avail(). The FAIL status in the HSTS
721c1733db1SRobert Mustacchi * register will get cleared before the next transaction begins and the
722c1733db1SRobert Mustacchi * HCTL KILL bit will be cleared when we issue the next command.
723c1733db1SRobert Mustacchi */
724c1733db1SRobert Mustacchi if (!pch->ps_req_done) {
725c1733db1SRobert Mustacchi uint8_t val = PCH_R_HCTL_SET_KILL(0, 1);
726c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCTL, val);
727c1733db1SRobert Mustacchi i2c_ctrl_io_error(&pch->ps_req->smbr_error,
728c1733db1SRobert Mustacchi I2C_CORE_E_CONTROLLER, I2C_CTRL_E_REQ_TO);
729c1733db1SRobert Mustacchi pch->ps_req_done = true;
730c1733db1SRobert Mustacchi }
731c1733db1SRobert Mustacchi }
732c1733db1SRobert Mustacchi
733c1733db1SRobert Mustacchi static void
pchsmbus_io_smbus(void * arg,uint32_t port,smbus_req_t * req)734c1733db1SRobert Mustacchi pchsmbus_io_smbus(void *arg, uint32_t port, smbus_req_t *req)
735c1733db1SRobert Mustacchi {
736c1733db1SRobert Mustacchi bool poll;
737c1733db1SRobert Mustacchi pchsmbus_t *pch = arg;
738c1733db1SRobert Mustacchi
739c1733db1SRobert Mustacchi ASSERT3U(port, ==, 0);
740c1733db1SRobert Mustacchi
741c1733db1SRobert Mustacchi mutex_enter(&pch->ps_mutex);
742c1733db1SRobert Mustacchi if (!pchsmbus_bus_avail(pch)) {
743c1733db1SRobert Mustacchi mutex_exit(&pch->ps_mutex);
744c1733db1SRobert Mustacchi i2c_ctrl_io_error(&req->smbr_error, I2C_CORE_E_CONTROLLER,
745c1733db1SRobert Mustacchi I2C_CTRL_E_BUS_BUSY);
746c1733db1SRobert Mustacchi return;
747c1733db1SRobert Mustacchi }
748c1733db1SRobert Mustacchi
749c1733db1SRobert Mustacchi ASSERT3P(pch->ps_req, ==, NULL);
750c1733db1SRobert Mustacchi pch->ps_req = req;
751c1733db1SRobert Mustacchi pch->ps_req_off = 0;
752c1733db1SRobert Mustacchi pch->ps_req_done = false;
753c1733db1SRobert Mustacchi
754c1733db1SRobert Mustacchi /*
755c1733db1SRobert Mustacchi * Determine whether or not we should use interrupts or poll for
756c1733db1SRobert Mustacchi * completion. We may have been asked to poll explicitly. We may also
757c1733db1SRobert Mustacchi * not have interrupt support.
758c1733db1SRobert Mustacchi */
759c1733db1SRobert Mustacchi poll = (req->smbr_flags & I2C_IO_REQ_F_POLL) != 0;
760c1733db1SRobert Mustacchi if (pch->ps_nintrs == 0)
761c1733db1SRobert Mustacchi poll = true;
762c1733db1SRobert Mustacchi
763c1733db1SRobert Mustacchi switch (req->smbr_op) {
764c1733db1SRobert Mustacchi case SMBUS_OP_QUICK_COMMAND:
765c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, (req->smbr_flags &
766c1733db1SRobert Mustacchi I2C_IO_REQ_F_QUICK_WRITE) != 0);
767c1733db1SRobert Mustacchi break;
768c1733db1SRobert Mustacchi case SMBUS_OP_SEND_BYTE:
769c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, true);
770c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCMD, req->smbr_wdata[0]);
771c1733db1SRobert Mustacchi break;
772c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BYTE:
773c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, true);
774c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCMD, req->smbr_cmd);
775c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HD0, req->smbr_wdata[0]);
776c1733db1SRobert Mustacchi break;
777c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_WORD:
778c1733db1SRobert Mustacchi case SMBUS_OP_PROCESS_CALL:
779c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, true);
780c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCMD, req->smbr_cmd);
781c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HD0, req->smbr_wdata[0]);
782c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HD1, req->smbr_wdata[1]);
783c1733db1SRobert Mustacchi break;
784c1733db1SRobert Mustacchi case SMBUS_OP_RECV_BYTE:
785c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, false);
786c1733db1SRobert Mustacchi break;
787c1733db1SRobert Mustacchi case SMBUS_OP_READ_BYTE:
788c1733db1SRobert Mustacchi case SMBUS_OP_READ_WORD:
789c1733db1SRobert Mustacchi pchsmbus_set_addr(pch, req, false);
790c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCMD, req->smbr_cmd);
791c1733db1SRobert Mustacchi break;
792c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BLOCK:
793c1733db1SRobert Mustacchi case SMBUS_OP_BLOCK_PROCESS_CALL:
794c1733db1SRobert Mustacchi case SMBUS_OP_READ_BLOCK:
795c1733db1SRobert Mustacchi case SMBUS_OP_I2C_WRITE_BLOCK:
796c1733db1SRobert Mustacchi case SMBUS_OP_I2C_READ_BLOCK:
797c1733db1SRobert Mustacchi pchsmbus_io_init_block(pch, req);
798c1733db1SRobert Mustacchi break;
799c1733db1SRobert Mustacchi case SMBUS_OP_HOST_NOTIFY:
800c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U32:
801c1733db1SRobert Mustacchi case SMBUS_OP_READ_U32:
802c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U64:
803c1733db1SRobert Mustacchi case SMBUS_OP_READ_U64:
804c1733db1SRobert Mustacchi default:
805c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "!framework passed unsupported "
806c1733db1SRobert Mustacchi "SMBus command 0x%x", req->smbr_op);
807c1733db1SRobert Mustacchi i2c_ctrl_io_error(&req->smbr_error, I2C_CORE_E_CONTROLLER,
808c1733db1SRobert Mustacchi I2C_CTRL_E_UNSUP_CMD);
809c1733db1SRobert Mustacchi goto done;
810c1733db1SRobert Mustacchi }
811c1733db1SRobert Mustacchi
812c1733db1SRobert Mustacchi /*
813c1733db1SRobert Mustacchi * Prepare to issue the command. We do this in a few different steps:
814c1733db1SRobert Mustacchi *
815c1733db1SRobert Mustacchi * 1) We set up command-specific parameters such as I2C enable. If the
816c1733db1SRobert Mustacchi * block enable is present, then it will have been already enabled.
817c1733db1SRobert Mustacchi * 2) Clear all interrupts.
818c1733db1SRobert Mustacchi * 3) Actually begin the transaction, indicating whether or not
819c1733db1SRobert Mustacchi * interrupts should occur.
820c1733db1SRobert Mustacchi * 4) Poll or wait for completion.
821c1733db1SRobert Mustacchi */
822c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HSTS, PCH_HSTS_CLEAR_PRE);
823c1733db1SRobert Mustacchi pch_smbus_cmd_t cmd = pchsmbus_req_to_cmd(req);
824c1733db1SRobert Mustacchi uint8_t ctl = PCH_R_HCTL_SET_CMD(0, cmd);
825c1733db1SRobert Mustacchi ctl = PCH_R_HCTL_SET_START(ctl, 1);
826c1733db1SRobert Mustacchi ctl = PCH_R_HCTL_SET_INT_EN(ctl, !poll);
827c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCTL, ctl);
828c1733db1SRobert Mustacchi pch->ps_req_hctl = ctl;
829c1733db1SRobert Mustacchi
830c1733db1SRobert Mustacchi pchsmbus_wait(pch, poll);
831c1733db1SRobert Mustacchi
832c1733db1SRobert Mustacchi done:
833c1733db1SRobert Mustacchi /*
834c1733db1SRobert Mustacchi * Now that this operation has completed, whether successful or not,
835c1733db1SRobert Mustacchi * restore the host configuration and block enable to our defaults.
836c1733db1SRobert Mustacchi */
837c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_RUN_I2C_EN) != 0) {
838c1733db1SRobert Mustacchi uint32_t val = pci_config_get32(pch->ps_cfg, PCH_R_PCIE_HCFG);
839c1733db1SRobert Mustacchi val = PCH_R_HCFG_SET_I2CEN(val, 0);
840c1733db1SRobert Mustacchi pci_config_put32(pch->ps_cfg, PCH_R_PCIE_HCFG, val);
841c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_RUN_I2C_EN;
842c1733db1SRobert Mustacchi }
843c1733db1SRobert Mustacchi
844c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_RUN_BUF_EN) != 0) {
845c1733db1SRobert Mustacchi uint8_t val = pchsmbus_read8(pch, PCH_R_BAR_AUXC);
846c1733db1SRobert Mustacchi val = PCH_R_AUXC_SET_E32B(val, 0);
847c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_AUXC, val);
848c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_RUN_BUF_EN;
849c1733db1SRobert Mustacchi }
850c1733db1SRobert Mustacchi
851c1733db1SRobert Mustacchi pch->ps_req = NULL;
852c1733db1SRobert Mustacchi pch->ps_req_off = 0;
853c1733db1SRobert Mustacchi pch->ps_req_hctl = 0;
854c1733db1SRobert Mustacchi pch->ps_req_done = false;
855c1733db1SRobert Mustacchi pch->ps_kill_err = I2C_CTRL_E_OK;
856c1733db1SRobert Mustacchi mutex_exit(&pch->ps_mutex);
857c1733db1SRobert Mustacchi }
858c1733db1SRobert Mustacchi
859c1733db1SRobert Mustacchi static const i2c_ctrl_ops_t pchsmbus_ctrl_ops = {
860c1733db1SRobert Mustacchi .i2c_port_name_f = i2c_ctrl_port_name_portno,
861c1733db1SRobert Mustacchi .i2c_io_smbus_f = pchsmbus_io_smbus,
862c1733db1SRobert Mustacchi .i2c_prop_info_f = pchsmbus_prop_info,
863c1733db1SRobert Mustacchi .i2c_prop_get_f = pchsmbus_prop_get
864c1733db1SRobert Mustacchi };
865c1733db1SRobert Mustacchi
866c1733db1SRobert Mustacchi static bool
pchsmbus_supported(pchsmbus_t * pch)867c1733db1SRobert Mustacchi pchsmbus_supported(pchsmbus_t *pch)
868c1733db1SRobert Mustacchi {
869c1733db1SRobert Mustacchi uint16_t id = pci_config_get16(pch->ps_cfg, PCI_CONF_VENID);
870c1733db1SRobert Mustacchi
871c1733db1SRobert Mustacchi if (id != PCH_SMBUS_VID_INTEL) {
872c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "found unsupported non-Intel "
873c1733db1SRobert Mustacchi "vendor ID: 0x%x", id);
874c1733db1SRobert Mustacchi return (false);
875c1733db1SRobert Mustacchi }
876c1733db1SRobert Mustacchi
877c1733db1SRobert Mustacchi id = pci_config_get16(pch->ps_cfg, PCI_CONF_DEVID);
878c1733db1SRobert Mustacchi for (size_t i = 0; i < ARRAY_SIZE(pchsmbus_feats); i++) {
879c1733db1SRobert Mustacchi if (id != pchsmbus_feats[i].pcm_did)
880c1733db1SRobert Mustacchi continue;
881c1733db1SRobert Mustacchi
882c1733db1SRobert Mustacchi pch->ps_feats = pchsmbus_feats[i].pcm_feat;
883c1733db1SRobert Mustacchi return (true);
884c1733db1SRobert Mustacchi }
885c1733db1SRobert Mustacchi
886c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "found unsupported device ID: 0x%x", id);
887c1733db1SRobert Mustacchi return (false);
888c1733db1SRobert Mustacchi }
889c1733db1SRobert Mustacchi
890c1733db1SRobert Mustacchi static bool
pchsmbus_setup_regs(pchsmbus_t * pch)891c1733db1SRobert Mustacchi pchsmbus_setup_regs(pchsmbus_t *pch)
892c1733db1SRobert Mustacchi {
893c1733db1SRobert Mustacchi int ret;
894c1733db1SRobert Mustacchi ddi_device_acc_attr_t attr;
895c1733db1SRobert Mustacchi
896c1733db1SRobert Mustacchi bzero(&attr, sizeof (attr));
897c1733db1SRobert Mustacchi attr.devacc_attr_version = DDI_DEVICE_ATTR_V1;
898c1733db1SRobert Mustacchi attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
899c1733db1SRobert Mustacchi attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
900c1733db1SRobert Mustacchi attr.devacc_attr_access = DDI_DEFAULT_ACC;
901c1733db1SRobert Mustacchi
902c1733db1SRobert Mustacchi if (ddi_dev_regsize(pch->ps_dip, PCHSMBUS_REGNO, &pch->ps_regsize) !=
903c1733db1SRobert Mustacchi DDI_SUCCESS) {
904c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to get regs[%u] size",
905c1733db1SRobert Mustacchi PCHSMBUS_REGNO);
906c1733db1SRobert Mustacchi return (false);
907c1733db1SRobert Mustacchi }
908c1733db1SRobert Mustacchi
909c1733db1SRobert Mustacchi ret = ddi_regs_map_setup(pch->ps_dip, PCHSMBUS_REGNO, &pch->ps_base,
910c1733db1SRobert Mustacchi 0, pch->ps_regsize, &attr, &pch->ps_regs);
911c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
912c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to map regs[%u]: %u",
913c1733db1SRobert Mustacchi PCHSMBUS_REGNO, ret);
914c1733db1SRobert Mustacchi return (false);
915c1733db1SRobert Mustacchi }
916c1733db1SRobert Mustacchi
917c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_REGS;
918c1733db1SRobert Mustacchi return (true);
919c1733db1SRobert Mustacchi }
920c1733db1SRobert Mustacchi
921c1733db1SRobert Mustacchi /*
922c1733db1SRobert Mustacchi * Go ahead and set up interrupts. It is possible that we don't get access to
923c1733db1SRobert Mustacchi * interrupts because firmware has enabled it to be delivered via a #SMI. If
924c1733db1SRobert Mustacchi * that's the case, we will just always rely on polling. Because that is the
925c1733db1SRobert Mustacchi * nature of this hardware, we treat the failure to detect interrupts as
926c1733db1SRobert Mustacchi * non-fatal, but if we find one and cannot actually set it up, we will fail
927c1733db1SRobert Mustacchi * then.
928c1733db1SRobert Mustacchi */
929c1733db1SRobert Mustacchi static bool
pchsmbus_setup_intr(pchsmbus_t * pch)930c1733db1SRobert Mustacchi pchsmbus_setup_intr(pchsmbus_t *pch)
931c1733db1SRobert Mustacchi {
932c1733db1SRobert Mustacchi int types, ret;
933c1733db1SRobert Mustacchi
934c1733db1SRobert Mustacchi pch->ps_nintrs = 0;
935c1733db1SRobert Mustacchi pch->ps_intr_pri = 0;
936c1733db1SRobert Mustacchi
937c1733db1SRobert Mustacchi /*
938c1733db1SRobert Mustacchi * If the SMI enable flag is set, that means that firmware is on the
939c1733db1SRobert Mustacchi * scene and we will just need to poll for completion rather than
940c1733db1SRobert Mustacchi * assuming we get interrupts. That's fine. Why it means that they won't
941c1733db1SRobert Mustacchi * fight us for the controller despite that is a deeper mystery.
942c1733db1SRobert Mustacchi */
943c1733db1SRobert Mustacchi if (PCH_R_HCFG_GET_SMI_EN(pch->ps_init_hcfg) != 0) {
944c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "!firmware has taken our "
945c1733db1SRobert Mustacchi "interrupt for itself via #SMI; limiting to polling");
946c1733db1SRobert Mustacchi return (true);
947c1733db1SRobert Mustacchi }
948c1733db1SRobert Mustacchi
949c1733db1SRobert Mustacchi ret = ddi_intr_get_supported_types(pch->ps_dip, &types);
950c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
951c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to get supported "
952c1733db1SRobert Mustacchi "interrupt types: 0x%x; limiting to polling", ret);
953c1733db1SRobert Mustacchi return (true);
954c1733db1SRobert Mustacchi }
955c1733db1SRobert Mustacchi
956c1733db1SRobert Mustacchi /*
957c1733db1SRobert Mustacchi * Hardware only supports INTx style fixed interrupts. That hasn't
958c1733db1SRobert Mustacchi * changed in 25 years of hardware. If we don't find a fixed interrupt
959c1733db1SRobert Mustacchi * that's that.
960c1733db1SRobert Mustacchi */
961c1733db1SRobert Mustacchi if ((types & DDI_INTR_TYPE_FIXED) == 0) {
962c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "missing support for fixed "
963c1733db1SRobert Mustacchi "interrupts: found 0x%x; limiting to polling", types);
964c1733db1SRobert Mustacchi return (true);
965c1733db1SRobert Mustacchi }
966c1733db1SRobert Mustacchi
967c1733db1SRobert Mustacchi ret = ddi_intr_alloc(pch->ps_dip, &pch->ps_intr_hdl,
968c1733db1SRobert Mustacchi DDI_INTR_TYPE_FIXED, 0, 1, &pch->ps_nintrs, DDI_INTR_ALLOC_STRICT);
969c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
970c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to allocate "
971c1733db1SRobert Mustacchi "interrupts: 0x%x", ret);
972c1733db1SRobert Mustacchi return (false);
973c1733db1SRobert Mustacchi }
974c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_INTR_ALLOC;
975c1733db1SRobert Mustacchi
976c1733db1SRobert Mustacchi ret = ddi_intr_add_handler(pch->ps_intr_hdl, pchsmbus_intr, pch, NULL);
977c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
978c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to add interrupt "
979c1733db1SRobert Mustacchi "handler: 0x%x", ret);
980c1733db1SRobert Mustacchi return (false);
981c1733db1SRobert Mustacchi }
982c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_INTR_HDL;
983c1733db1SRobert Mustacchi
984c1733db1SRobert Mustacchi ret = ddi_intr_get_pri(pch->ps_intr_hdl, &pch->ps_intr_pri);
985c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
986c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to get interrupt "
987c1733db1SRobert Mustacchi "priority");
988c1733db1SRobert Mustacchi return (false);
989c1733db1SRobert Mustacchi }
990c1733db1SRobert Mustacchi
991c1733db1SRobert Mustacchi return (true);
992c1733db1SRobert Mustacchi }
993c1733db1SRobert Mustacchi
994c1733db1SRobert Mustacchi /*
995c1733db1SRobert Mustacchi * Go through and set up the controller for general use. In particular, there
996c1733db1SRobert Mustacchi * are a few things that we go through and make sure are set in a way that makes
997c1733db1SRobert Mustacchi * sense for us:
998c1733db1SRobert Mustacchi *
999c1733db1SRobert Mustacchi * - We always disable automatic PEC. The Auxiliary 32 byte buffer control will
1000c1733db1SRobert Mustacchi * be enabled when it can be used.
1001c1733db1SRobert Mustacchi * - We disable any events that can be generated by the target.
1002c1733db1SRobert Mustacchi * - We make sure that SMBus timing is enabled by default.
1003c1733db1SRobert Mustacchi * - Ensure that interrupts are disabled and that the PEC feature is not set.
1004c1733db1SRobert Mustacchi * Interrupts will be enabled when we actually enable commands.
1005c1733db1SRobert Mustacchi * - We actually enable the controller.
1006c1733db1SRobert Mustacchi */
1007c1733db1SRobert Mustacchi static void
pchsmbus_ctrl_init(pchsmbus_t * pch)1008c1733db1SRobert Mustacchi pchsmbus_ctrl_init(pchsmbus_t *pch)
1009c1733db1SRobert Mustacchi {
1010c1733db1SRobert Mustacchi if ((pch->ps_feats & PCH_SMBUS_FEAT_HW_PEC) != 0) {
1011c1733db1SRobert Mustacchi uint8_t val = pchsmbus_read8(pch, PCH_R_BAR_AUXC);
1012c1733db1SRobert Mustacchi val = PCH_R_AUXC_SET_AAC(val, 0);
1013c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_AUXC, val);
1014c1733db1SRobert Mustacchi }
1015c1733db1SRobert Mustacchi
1016c1733db1SRobert Mustacchi if ((pch->ps_feats & PCH_SMBUS_FEAT_TARG_NOTIFY) != 0) {
1017c1733db1SRobert Mustacchi pch->ps_init_scmd = pchsmbus_read8(pch, PCH_R_BAR_SCMD);
1018c1733db1SRobert Mustacchi
1019c1733db1SRobert Mustacchi uint8_t val = PCH_R_SCMD_SET_SMB_D(pch->ps_init_scmd, 1);
1020c1733db1SRobert Mustacchi val = PCH_R_SCMD_SET_HNI(val, 0);
1021c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_SCMD, 0);
1022c1733db1SRobert Mustacchi }
1023c1733db1SRobert Mustacchi
1024c1733db1SRobert Mustacchi /*
1025c1733db1SRobert Mustacchi * Save the initial control register to restore later. However, don't
1026c1733db1SRobert Mustacchi * save the kill bit which stops transactions. At this point, make sure
1027c1733db1SRobert Mustacchi * interrupts and related activity are all disabled.
1028c1733db1SRobert Mustacchi */
1029c1733db1SRobert Mustacchi pch->ps_init_hctl = pchsmbus_read8(pch, PCH_R_BAR_HCTL);
1030c1733db1SRobert Mustacchi pch->ps_init_hctl = PCH_R_HCTL_SET_KILL(pch->ps_init_hctl, 0);
1031c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCTL, 0);
1032c1733db1SRobert Mustacchi
1033c1733db1SRobert Mustacchi uint32_t val = pch->ps_init_hcfg;
1034c1733db1SRobert Mustacchi val = PCH_R_HCFG_SET_EN(val, 1);
1035c1733db1SRobert Mustacchi val = PCH_R_HCFG_SET_I2CEN(val, PCH_R_HCFG_I2CEN_SMBUS);
1036c1733db1SRobert Mustacchi pci_config_put32(pch->ps_cfg, PCH_R_PCIE_HCFG, val);
1037c1733db1SRobert Mustacchi
1038c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_CTRL;
1039c1733db1SRobert Mustacchi }
1040c1733db1SRobert Mustacchi
1041c1733db1SRobert Mustacchi static bool
pchsmbus_enable_intr(pchsmbus_t * pch)1042c1733db1SRobert Mustacchi pchsmbus_enable_intr(pchsmbus_t *pch)
1043c1733db1SRobert Mustacchi {
1044c1733db1SRobert Mustacchi int ret = ddi_intr_enable(pch->ps_intr_hdl);
1045c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
1046c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to enable interrupt "
1047c1733db1SRobert Mustacchi "handler: %d", ret);
1048c1733db1SRobert Mustacchi return (false);
1049c1733db1SRobert Mustacchi }
1050c1733db1SRobert Mustacchi
1051c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_INTR_EN;
1052c1733db1SRobert Mustacchi return (true);
1053c1733db1SRobert Mustacchi }
1054c1733db1SRobert Mustacchi
1055c1733db1SRobert Mustacchi static bool
pchsmbus_register(pchsmbus_t * pch)1056c1733db1SRobert Mustacchi pchsmbus_register(pchsmbus_t *pch)
1057c1733db1SRobert Mustacchi {
1058c1733db1SRobert Mustacchi i2c_ctrl_reg_error_t ret;
1059c1733db1SRobert Mustacchi i2c_ctrl_register_t *reg;
1060c1733db1SRobert Mustacchi
1061c1733db1SRobert Mustacchi ret = i2c_ctrl_register_alloc(I2C_CTRL_PROVIDER, ®);
1062c1733db1SRobert Mustacchi if (ret != 0) {
1063c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to allocate i2c "
1064c1733db1SRobert Mustacchi "controller registration structure: 0x%x", ret);
1065c1733db1SRobert Mustacchi return (false);
1066c1733db1SRobert Mustacchi }
1067c1733db1SRobert Mustacchi
1068c1733db1SRobert Mustacchi reg->ic_type = I2C_CTRL_TYPE_SMBUS;
1069c1733db1SRobert Mustacchi reg->ic_nports = 1;
1070c1733db1SRobert Mustacchi reg->ic_dip = pch->ps_dip;
1071c1733db1SRobert Mustacchi reg->ic_drv = pch;
1072c1733db1SRobert Mustacchi reg->ic_ops = &pchsmbus_ctrl_ops;
1073c1733db1SRobert Mustacchi
1074c1733db1SRobert Mustacchi ret = i2c_ctrl_register(reg, &pch->ps_hdl);
1075c1733db1SRobert Mustacchi i2c_ctrl_register_free(reg);
1076c1733db1SRobert Mustacchi if (ret != 0) {
1077c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to register with i2c "
1078c1733db1SRobert Mustacchi "framework: 0x%x", ret);
1079c1733db1SRobert Mustacchi return (false);
1080c1733db1SRobert Mustacchi }
1081c1733db1SRobert Mustacchi
1082c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_I2C;
1083c1733db1SRobert Mustacchi return (true);
1084c1733db1SRobert Mustacchi }
1085c1733db1SRobert Mustacchi
1086c1733db1SRobert Mustacchi static void
pchsmbus_cleanup(pchsmbus_t * pch)1087c1733db1SRobert Mustacchi pchsmbus_cleanup(pchsmbus_t *pch)
1088c1733db1SRobert Mustacchi {
1089c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_INTR_EN) != 0) {
1090c1733db1SRobert Mustacchi /*
1091c1733db1SRobert Mustacchi * If this fails while tearing down, there isn't much we can do.
1092c1733db1SRobert Mustacchi */
1093c1733db1SRobert Mustacchi int ret = ddi_intr_disable(pch->ps_intr_hdl);
1094c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
1095c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to disable "
1096c1733db1SRobert Mustacchi "interrupt handler: %d", ret);
1097c1733db1SRobert Mustacchi }
1098c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_INTR_EN;
1099c1733db1SRobert Mustacchi }
1100c1733db1SRobert Mustacchi
1101c1733db1SRobert Mustacchi /*
1102c1733db1SRobert Mustacchi * We restore several of the controllers original values as the BIOS may
1103c1733db1SRobert Mustacchi * use this device and can rely on it.
1104c1733db1SRobert Mustacchi */
1105c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_CTRL) != 0) {
1106c1733db1SRobert Mustacchi if ((pch->ps_feats & PCH_SMBUS_FEAT_TARG_NOTIFY) != 0) {
1107c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_SCMD, pch->ps_init_scmd);
1108c1733db1SRobert Mustacchi }
1109c1733db1SRobert Mustacchi
1110c1733db1SRobert Mustacchi pchsmbus_write8(pch, PCH_R_BAR_HCTL, pch->ps_init_hctl);
1111c1733db1SRobert Mustacchi pci_config_put32(pch->ps_cfg, PCH_R_PCIE_HCFG,
1112c1733db1SRobert Mustacchi pch->ps_init_hcfg);
1113c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_CTRL;
1114c1733db1SRobert Mustacchi }
1115c1733db1SRobert Mustacchi
1116c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_SYNC) != 0) {
1117c1733db1SRobert Mustacchi cv_destroy(&pch->ps_cv);
1118c1733db1SRobert Mustacchi mutex_destroy(&pch->ps_mutex);
1119c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_SYNC;
1120c1733db1SRobert Mustacchi }
1121c1733db1SRobert Mustacchi
1122c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_INTR_HDL) != 0) {
1123c1733db1SRobert Mustacchi int ret = ddi_intr_remove_handler(pch->ps_intr_hdl);
1124c1733db1SRobert Mustacchi if (ret != 0) {
1125c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to remove "
1126c1733db1SRobert Mustacchi "interrupt handler: 0x%x", ret);
1127c1733db1SRobert Mustacchi }
1128c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_INTR_HDL;
1129c1733db1SRobert Mustacchi }
1130c1733db1SRobert Mustacchi
1131c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_INTR_ALLOC) != 0) {
1132c1733db1SRobert Mustacchi int ret = ddi_intr_free(pch->ps_intr_hdl);
1133c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
1134c1733db1SRobert Mustacchi dev_err(pch->ps_dip, CE_WARN, "failed to free "
1135c1733db1SRobert Mustacchi "device interrupt: 0x%x", ret);
1136c1733db1SRobert Mustacchi }
1137c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_INTR_ALLOC;
1138c1733db1SRobert Mustacchi }
1139c1733db1SRobert Mustacchi
1140c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_REGS) != 0) {
1141c1733db1SRobert Mustacchi ddi_regs_map_free(&pch->ps_regs);
1142c1733db1SRobert Mustacchi pch->ps_base = NULL;
1143c1733db1SRobert Mustacchi pch->ps_regsize = 0;
1144c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_REGS;
1145c1733db1SRobert Mustacchi }
1146c1733db1SRobert Mustacchi
1147c1733db1SRobert Mustacchi if ((pch->ps_init & PCHSMBUS_INIT_PCI) != 0) {
1148c1733db1SRobert Mustacchi pci_config_teardown(&pch->ps_cfg);
1149c1733db1SRobert Mustacchi pch->ps_cfg = NULL;
1150c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_PCI;
1151c1733db1SRobert Mustacchi }
1152c1733db1SRobert Mustacchi
1153c1733db1SRobert Mustacchi ASSERT0(pch->ps_init);
1154c1733db1SRobert Mustacchi ddi_set_driver_private(pch->ps_dip, NULL);
1155c1733db1SRobert Mustacchi kmem_free(pch, sizeof (pchsmbus_t));
1156c1733db1SRobert Mustacchi }
1157c1733db1SRobert Mustacchi
1158c1733db1SRobert Mustacchi int
pchsmbus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1159c1733db1SRobert Mustacchi pchsmbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1160c1733db1SRobert Mustacchi {
1161c1733db1SRobert Mustacchi pchsmbus_t *pch;
1162c1733db1SRobert Mustacchi
1163c1733db1SRobert Mustacchi switch (cmd) {
1164c1733db1SRobert Mustacchi case DDI_ATTACH:
1165c1733db1SRobert Mustacchi break;
1166c1733db1SRobert Mustacchi case DDI_RESUME:
1167c1733db1SRobert Mustacchi default:
1168c1733db1SRobert Mustacchi return (DDI_FAILURE);
1169c1733db1SRobert Mustacchi }
1170c1733db1SRobert Mustacchi
1171c1733db1SRobert Mustacchi pch = kmem_zalloc(sizeof (pchsmbus_t), KM_SLEEP);
1172c1733db1SRobert Mustacchi pch->ps_dip = dip;
1173c1733db1SRobert Mustacchi ddi_set_driver_private(dip, pch);
1174c1733db1SRobert Mustacchi
1175c1733db1SRobert Mustacchi if (pci_config_setup(dip, &pch->ps_cfg) != DDI_SUCCESS) {
1176c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to set up config space");
1177c1733db1SRobert Mustacchi goto cleanup;
1178c1733db1SRobert Mustacchi }
1179c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_PCI;
1180c1733db1SRobert Mustacchi
1181c1733db1SRobert Mustacchi if (!pchsmbus_supported(pch))
1182c1733db1SRobert Mustacchi goto cleanup;
1183c1733db1SRobert Mustacchi
1184c1733db1SRobert Mustacchi if (!pchsmbus_setup_regs(pch))
1185c1733db1SRobert Mustacchi goto cleanup;
1186c1733db1SRobert Mustacchi
1187c1733db1SRobert Mustacchi /*
1188c1733db1SRobert Mustacchi * Snapshot the original value of the host configuration register. This
1189c1733db1SRobert Mustacchi * is something that some systems will restore on detach as sometimes
1190c1733db1SRobert Mustacchi * firmware uses this controller. In addition, we need this to determine
1191c1733db1SRobert Mustacchi * if we have interrupts available to us.
1192c1733db1SRobert Mustacchi */
1193c1733db1SRobert Mustacchi pch->ps_init_hcfg = pci_config_get32(pch->ps_cfg, PCH_R_PCIE_HCFG);
1194c1733db1SRobert Mustacchi
1195c1733db1SRobert Mustacchi if (!pchsmbus_setup_intr(pch))
1196c1733db1SRobert Mustacchi goto cleanup;
1197c1733db1SRobert Mustacchi
1198c1733db1SRobert Mustacchi /*
1199c1733db1SRobert Mustacchi * Now that we (potentially) have our interrupt. Go ahead and get our
1200c1733db1SRobert Mustacchi * intrrupt and CV. If we don't have an interrupt this'll turn into a
1201c1733db1SRobert Mustacchi * NULL.
1202c1733db1SRobert Mustacchi */
1203c1733db1SRobert Mustacchi mutex_init(&pch->ps_mutex, NULL, MUTEX_DRIVER,
1204c1733db1SRobert Mustacchi DDI_INTR_PRI(pch->ps_intr_pri));
1205c1733db1SRobert Mustacchi cv_init(&pch->ps_cv, NULL, CV_DRIVER, NULL);
1206c1733db1SRobert Mustacchi pch->ps_init |= PCHSMBUS_INIT_SYNC;
1207c1733db1SRobert Mustacchi
1208c1733db1SRobert Mustacchi pchsmbus_ctrl_init(pch);
1209c1733db1SRobert Mustacchi
1210c1733db1SRobert Mustacchi if (!pchsmbus_enable_intr(pch))
1211c1733db1SRobert Mustacchi goto cleanup;
1212c1733db1SRobert Mustacchi
1213c1733db1SRobert Mustacchi if (!pchsmbus_register(pch))
1214c1733db1SRobert Mustacchi goto cleanup;
1215c1733db1SRobert Mustacchi
1216c1733db1SRobert Mustacchi return (DDI_SUCCESS);
1217c1733db1SRobert Mustacchi
1218c1733db1SRobert Mustacchi cleanup:
1219c1733db1SRobert Mustacchi pchsmbus_cleanup(pch);
1220c1733db1SRobert Mustacchi return (DDI_FAILURE);
1221c1733db1SRobert Mustacchi }
1222c1733db1SRobert Mustacchi
1223c1733db1SRobert Mustacchi int
pchsmbus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1224c1733db1SRobert Mustacchi pchsmbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1225c1733db1SRobert Mustacchi {
1226c1733db1SRobert Mustacchi pchsmbus_t *pch;
1227c1733db1SRobert Mustacchi
1228c1733db1SRobert Mustacchi switch (cmd) {
1229c1733db1SRobert Mustacchi case DDI_DETACH:
1230c1733db1SRobert Mustacchi break;
1231c1733db1SRobert Mustacchi case DDI_SUSPEND:
1232c1733db1SRobert Mustacchi default:
1233c1733db1SRobert Mustacchi return (DDI_FAILURE);
1234c1733db1SRobert Mustacchi }
1235c1733db1SRobert Mustacchi
1236c1733db1SRobert Mustacchi pch = ddi_get_driver_private(dip);
1237c1733db1SRobert Mustacchi if (pch == NULL) {
1238c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "asked to detach, but missing private "
1239c1733db1SRobert Mustacchi "data");
1240c1733db1SRobert Mustacchi return (DDI_FAILURE);
1241c1733db1SRobert Mustacchi }
1242c1733db1SRobert Mustacchi
1243c1733db1SRobert Mustacchi VERIFY3P(pch->ps_dip, ==, dip);
1244c1733db1SRobert Mustacchi i2c_ctrl_reg_error_t ret = i2c_ctrl_unregister(pch->ps_hdl);
1245c1733db1SRobert Mustacchi if (ret != 0) {
1246c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to unregister from i2c "
1247c1733db1SRobert Mustacchi "framework 0x%x", ret);
1248c1733db1SRobert Mustacchi return (DDI_FAILURE);
1249c1733db1SRobert Mustacchi }
1250c1733db1SRobert Mustacchi pch->ps_init &= ~PCHSMBUS_INIT_I2C;
1251c1733db1SRobert Mustacchi pchsmbus_cleanup(pch);
1252c1733db1SRobert Mustacchi
1253c1733db1SRobert Mustacchi return (DDI_SUCCESS);
1254c1733db1SRobert Mustacchi }
1255c1733db1SRobert Mustacchi
1256c1733db1SRobert Mustacchi static struct dev_ops pchsmbus_dev_ops = {
1257c1733db1SRobert Mustacchi .devo_rev = DEVO_REV,
1258c1733db1SRobert Mustacchi .devo_refcnt = 0,
1259c1733db1SRobert Mustacchi .devo_identify = nulldev,
1260c1733db1SRobert Mustacchi .devo_probe = nulldev,
1261c1733db1SRobert Mustacchi .devo_attach = pchsmbus_attach,
1262c1733db1SRobert Mustacchi .devo_detach = pchsmbus_detach,
1263c1733db1SRobert Mustacchi .devo_reset = nodev,
1264c1733db1SRobert Mustacchi .devo_quiesce = ddi_quiesce_not_supported,
1265c1733db1SRobert Mustacchi };
1266c1733db1SRobert Mustacchi
1267c1733db1SRobert Mustacchi static struct modldrv pchsmbus_modldrv = {
1268c1733db1SRobert Mustacchi .drv_modops = &mod_driverops,
1269c1733db1SRobert Mustacchi .drv_linkinfo = "Intel ICH/PCH SMBus Controller",
1270c1733db1SRobert Mustacchi .drv_dev_ops = &pchsmbus_dev_ops
1271c1733db1SRobert Mustacchi };
1272c1733db1SRobert Mustacchi
1273c1733db1SRobert Mustacchi static struct modlinkage pchsmbus_modlinkage = {
1274c1733db1SRobert Mustacchi .ml_rev = MODREV_1,
1275c1733db1SRobert Mustacchi .ml_linkage = { &pchsmbus_modldrv, NULL }
1276c1733db1SRobert Mustacchi };
1277c1733db1SRobert Mustacchi
1278c1733db1SRobert Mustacchi int
_init(void)1279c1733db1SRobert Mustacchi _init(void)
1280c1733db1SRobert Mustacchi {
1281c1733db1SRobert Mustacchi int ret;
1282c1733db1SRobert Mustacchi
1283c1733db1SRobert Mustacchi i2c_ctrl_mod_init(&pchsmbus_dev_ops);
1284c1733db1SRobert Mustacchi if ((ret = mod_install(&pchsmbus_modlinkage)) != 0) {
1285c1733db1SRobert Mustacchi i2c_ctrl_mod_fini(&pchsmbus_dev_ops);
1286c1733db1SRobert Mustacchi }
1287c1733db1SRobert Mustacchi
1288c1733db1SRobert Mustacchi return (ret);
1289c1733db1SRobert Mustacchi }
1290c1733db1SRobert Mustacchi
1291c1733db1SRobert Mustacchi int
_info(struct modinfo * modinfop)1292c1733db1SRobert Mustacchi _info(struct modinfo *modinfop)
1293c1733db1SRobert Mustacchi {
1294c1733db1SRobert Mustacchi return (mod_info(&pchsmbus_modlinkage, modinfop));
1295c1733db1SRobert Mustacchi }
1296c1733db1SRobert Mustacchi
1297c1733db1SRobert Mustacchi int
_fini(void)1298c1733db1SRobert Mustacchi _fini(void)
1299c1733db1SRobert Mustacchi {
1300c1733db1SRobert Mustacchi int ret;
1301c1733db1SRobert Mustacchi
1302c1733db1SRobert Mustacchi if ((ret = mod_remove(&pchsmbus_modlinkage)) == 0) {
1303c1733db1SRobert Mustacchi i2c_ctrl_mod_fini(&pchsmbus_dev_ops);
1304c1733db1SRobert Mustacchi }
1305c1733db1SRobert Mustacchi
1306c1733db1SRobert Mustacchi return (ret);
1307c1733db1SRobert Mustacchi }
1308