1*c1733db1SRobert Mustacchi /*
2*c1733db1SRobert Mustacchi * This file and its contents are supplied under the terms of the
3*c1733db1SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*c1733db1SRobert Mustacchi * You may only use this file in accordance with the terms of version
5*c1733db1SRobert Mustacchi * 1.0 of the CDDL.
6*c1733db1SRobert Mustacchi *
7*c1733db1SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*c1733db1SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*c1733db1SRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*c1733db1SRobert Mustacchi */
11*c1733db1SRobert Mustacchi
12*c1733db1SRobert Mustacchi /*
13*c1733db1SRobert Mustacchi * Copyright 2025 Oxide Computer Company
14*c1733db1SRobert Mustacchi */
15*c1733db1SRobert Mustacchi
16*c1733db1SRobert Mustacchi /*
17*c1733db1SRobert Mustacchi * Intel SMBus Message Transport controller driver.
18*c1733db1SRobert Mustacchi *
19*c1733db1SRobert Mustacchi * This is a DMA based SMBus controller that is found in some of the various
20*c1733db1SRobert Mustacchi * Atom and Xeon-D based platforms. The hardware divides registers into three
21*c1733db1SRobert Mustacchi * rough groups. There are the general registers, controller specific registers,
22*c1733db1SRobert Mustacchi * and target specific registers. We don't implement the target.
23*c1733db1SRobert Mustacchi *
24*c1733db1SRobert Mustacchi * The device can support a ring of DMA transfers. We only will have one
25*c1733db1SRobert Mustacchi * outstanding at a time, so we go with a simple length of 4 entries and have a
26*c1733db1SRobert Mustacchi * single data DMA buffer that is used by all commands.
27*c1733db1SRobert Mustacchi *
28*c1733db1SRobert Mustacchi * Unlike the PCH-based SMBus controller, this controller supports both SMBus
29*c1733db1SRobert Mustacchi * 2.0 commands and can perform arbitrary commands over I2C.
30*c1733db1SRobert Mustacchi */
31*c1733db1SRobert Mustacchi
32*c1733db1SRobert Mustacchi #include <sys/modctl.h>
33*c1733db1SRobert Mustacchi #include <sys/conf.h>
34*c1733db1SRobert Mustacchi #include <sys/devops.h>
35*c1733db1SRobert Mustacchi #include <sys/ddi.h>
36*c1733db1SRobert Mustacchi #include <sys/sunddi.h>
37*c1733db1SRobert Mustacchi #include <sys/pci.h>
38*c1733db1SRobert Mustacchi #include <sys/sysmacros.h>
39*c1733db1SRobert Mustacchi
40*c1733db1SRobert Mustacchi #include <sys/i2c/controller.h>
41*c1733db1SRobert Mustacchi #include "ismt.h"
42*c1733db1SRobert Mustacchi
43*c1733db1SRobert Mustacchi /*
44*c1733db1SRobert Mustacchi * The controller has a single BAR, SMTBAR, which is found in the BAR 0/1. This
45*c1733db1SRobert Mustacchi * translates to reg[1] as reg[0] contains the config space information about
46*c1733db1SRobert Mustacchi * the device.
47*c1733db1SRobert Mustacchi */
48*c1733db1SRobert Mustacchi #define ISMT_REGNO 1
49*c1733db1SRobert Mustacchi
50*c1733db1SRobert Mustacchi /*
51*c1733db1SRobert Mustacchi * Because we never use the offset and address for syncing, we want to cast the
52*c1733db1SRobert Mustacchi * DMA sync call to void, but lets be paranoid on debug.
53*c1733db1SRobert Mustacchi */
54*c1733db1SRobert Mustacchi #ifdef DEBUG
55*c1733db1SRobert Mustacchi #define ISMT_DMA_SYNC(buf, flag) ASSERT0(ddi_dma_sync((buf).id_hdl, \
56*c1733db1SRobert Mustacchi 0, 0, flag))
57*c1733db1SRobert Mustacchi #else
58*c1733db1SRobert Mustacchi #define ISMT_DMA_SYNC(buf, flag) (void) ddi_dma_sync((buf).id_hdl, \
59*c1733db1SRobert Mustacchi 0, 0, flag)
60*c1733db1SRobert Mustacchi #endif /* DEBUG */
61*c1733db1SRobert Mustacchi
62*c1733db1SRobert Mustacchi
63*c1733db1SRobert Mustacchi /*
64*c1733db1SRobert Mustacchi * Allocation sizes for our ring and DMA data buffer. We size the ring at 4
65*c1733db1SRobert Mustacchi * entries (though it seems like we could probably get away with just one). The
66*c1733db1SRobert Mustacchi * data buffer we size at 128 bytes, which covers both the maximum read and
67*c1733db1SRobert Mustacchi * maximum write in one go (though we shouldn't exceed it either one). The last
68*c1733db1SRobert Mustacchi * one of these, the interrupt cause log size is the hardest. It defitely is
69*c1733db1SRobert Mustacchi * written before every interrupt, but whether we need one of these per entry in
70*c1733db1SRobert Mustacchi * the log or it just clobbers the last location is unclear. It doesn't actually
71*c1733db1SRobert Mustacchi * ask for a size so we just double the number of ring entries.
72*c1733db1SRobert Mustacchi */
73*c1733db1SRobert Mustacchi #define ISMT_RING_NENTS 4
74*c1733db1SRobert Mustacchi #define ISMT_RING_DMA_SIZE (ISMT_RING_NENTS * sizeof (ismt_desc_t))
75*c1733db1SRobert Mustacchi #define ISMT_DATA_BUF_SIZE 128U
76*c1733db1SRobert Mustacchi #define ISMT_ICL_DMA_SIZE (ISMT_RING_NENTS * sizeof (uint32_t) * 2)
77*c1733db1SRobert Mustacchi
78*c1733db1SRobert Mustacchi typedef enum {
79*c1733db1SRobert Mustacchi ISMT_INIT_PCI = 1 << 0,
80*c1733db1SRobert Mustacchi ISMT_INIT_REGS = 1 << 1,
81*c1733db1SRobert Mustacchi ISMT_INIT_INTR_ALLOC = 1 << 2,
82*c1733db1SRobert Mustacchi ISMT_INIT_INTR_HDL = 1 << 3,
83*c1733db1SRobert Mustacchi ISMT_INIT_SYNC = 1 << 4,
84*c1733db1SRobert Mustacchi ISMT_INIT_INTR_EN = 1 << 5,
85*c1733db1SRobert Mustacchi ISMT_INIT_I2C = 1 << 6
86*c1733db1SRobert Mustacchi } ismt_init_t;
87*c1733db1SRobert Mustacchi
88*c1733db1SRobert Mustacchi typedef struct {
89*c1733db1SRobert Mustacchi ddi_dma_handle_t id_hdl;
90*c1733db1SRobert Mustacchi caddr_t id_va;
91*c1733db1SRobert Mustacchi ddi_acc_handle_t id_acc;
92*c1733db1SRobert Mustacchi size_t id_alloc_len;
93*c1733db1SRobert Mustacchi size_t id_size;
94*c1733db1SRobert Mustacchi } ismt_dma_t;
95*c1733db1SRobert Mustacchi
96*c1733db1SRobert Mustacchi typedef struct {
97*c1733db1SRobert Mustacchi dev_info_t *ismt_dip;
98*c1733db1SRobert Mustacchi ddi_acc_handle_t ismt_cfg;
99*c1733db1SRobert Mustacchi ismt_init_t ismt_init;
100*c1733db1SRobert Mustacchi /*
101*c1733db1SRobert Mustacchi * Register related data
102*c1733db1SRobert Mustacchi */
103*c1733db1SRobert Mustacchi caddr_t ismt_base;
104*c1733db1SRobert Mustacchi off_t ismt_regsize;
105*c1733db1SRobert Mustacchi ddi_acc_handle_t ismt_regs;
106*c1733db1SRobert Mustacchi /*
107*c1733db1SRobert Mustacchi * DMA Information
108*c1733db1SRobert Mustacchi */
109*c1733db1SRobert Mustacchi ismt_dma_t ismt_ring_dma;
110*c1733db1SRobert Mustacchi ismt_dma_t ismt_data_dma;
111*c1733db1SRobert Mustacchi ismt_dma_t ismt_icl_dma;
112*c1733db1SRobert Mustacchi /*
113*c1733db1SRobert Mustacchi * Interrupt data
114*c1733db1SRobert Mustacchi */
115*c1733db1SRobert Mustacchi int ismt_nintrs;
116*c1733db1SRobert Mustacchi int ismt_itype;
117*c1733db1SRobert Mustacchi ddi_intr_handle_t ismt_intr_hdl;
118*c1733db1SRobert Mustacchi uint_t ismt_intr_pri;
119*c1733db1SRobert Mustacchi /*
120*c1733db1SRobert Mustacchi * Request and framework synchronization.
121*c1733db1SRobert Mustacchi */
122*c1733db1SRobert Mustacchi i2c_speed_t ismt_speed;
123*c1733db1SRobert Mustacchi kmutex_t ismt_mutex;
124*c1733db1SRobert Mustacchi kcondvar_t ismt_cv;
125*c1733db1SRobert Mustacchi i2c_ctrl_hdl_t *ismt_hdl;
126*c1733db1SRobert Mustacchi uint32_t ismt_head;
127*c1733db1SRobert Mustacchi uint32_t ismt_tail;
128*c1733db1SRobert Mustacchi ismt_desc_t *ismt_ring;
129*c1733db1SRobert Mustacchi smbus_req_t *ismt_req;
130*c1733db1SRobert Mustacchi i2c_req_t *ismt_i2creq;
131*c1733db1SRobert Mustacchi i2c_error_t *ismt_err;
132*c1733db1SRobert Mustacchi bool ismt_req_done;
133*c1733db1SRobert Mustacchi } ismt_t;
134*c1733db1SRobert Mustacchi
135*c1733db1SRobert Mustacchi /*
136*c1733db1SRobert Mustacchi * Consolidated DMA attributes for the descriptor ring and the data buffer. We
137*c1733db1SRobert Mustacchi * use the stricter requirements from each because we don't actually allocate
138*c1733db1SRobert Mustacchi * that much DMA memory here to make this simpler.
139*c1733db1SRobert Mustacchi */
140*c1733db1SRobert Mustacchi static const ddi_dma_attr_t ismt_dma_attr = {
141*c1733db1SRobert Mustacchi .dma_attr_version = DMA_ATTR_V0,
142*c1733db1SRobert Mustacchi /*
143*c1733db1SRobert Mustacchi * Our DMA attributes can appear anywhere in 64-bit space.
144*c1733db1SRobert Mustacchi */
145*c1733db1SRobert Mustacchi .dma_attr_addr_lo = 0,
146*c1733db1SRobert Mustacchi .dma_attr_addr_hi = UINT64_MAX,
147*c1733db1SRobert Mustacchi /*
148*c1733db1SRobert Mustacchi * Up to 255 bytes are allowed to be specified for transmit / recieve,
149*c1733db1SRobert Mustacchi * where as the descriptor ring allows for up to 256 16 byte
150*c1733db1SRobert Mustacchi * descriptors. We use the latter for here, with the knowledge that
151*c1733db1SRobert Mustacchi * we're never allocating more than an amount that can fit due to the
152*c1733db1SRobert Mustacchi * maxxfer value below.
153*c1733db1SRobert Mustacchi */
154*c1733db1SRobert Mustacchi .dma_attr_count_max = 256 * sizeof (ismt_desc_t),
155*c1733db1SRobert Mustacchi /*
156*c1733db1SRobert Mustacchi * The descriptor ring requires 64 byte alignment, where as the data
157*c1733db1SRobert Mustacchi * buffer requires byte alignment. Use 64 byte alignment.
158*c1733db1SRobert Mustacchi */
159*c1733db1SRobert Mustacchi .dma_attr_align = 0x40,
160*c1733db1SRobert Mustacchi /*
161*c1733db1SRobert Mustacchi * Cargo culted burst sizes as PCIe probably doens't have quite the same
162*c1733db1SRobert Mustacchi * concerns.
163*c1733db1SRobert Mustacchi */
164*c1733db1SRobert Mustacchi .dma_attr_burstsizes = 0xfff,
165*c1733db1SRobert Mustacchi /*
166*c1733db1SRobert Mustacchi * We set the minimum and maximum to the sizes here. The size limits are
167*c1733db1SRobert Mustacchi * really set by PCIe breaking up transactions, not anything in the
168*c1733db1SRobert Mustacchi * kernel. Therefore we set the maximum to the same as
169*c1733db1SRobert Mustacchi * dma_attr_count_max, partially so we can avoid complaints about DMA
170*c1733db1SRobert Mustacchi * less than a page by x86 rootnex.
171*c1733db1SRobert Mustacchi */
172*c1733db1SRobert Mustacchi .dma_attr_minxfer = 1,
173*c1733db1SRobert Mustacchi .dma_attr_maxxfer = 256 * sizeof (ismt_desc_t),
174*c1733db1SRobert Mustacchi /*
175*c1733db1SRobert Mustacchi * Their are no segment restrictions and only one cookie can be used.
176*c1733db1SRobert Mustacchi * For the granularity we basically set this to 1 because everything we
177*c1733db1SRobert Mustacchi * allocate will be a multiple of this and we only have one cookie so it
178*c1733db1SRobert Mustacchi * won't really be split..
179*c1733db1SRobert Mustacchi */
180*c1733db1SRobert Mustacchi .dma_attr_seg = UINT64_MAX,
181*c1733db1SRobert Mustacchi .dma_attr_sgllen = 1,
182*c1733db1SRobert Mustacchi .dma_attr_granular = 1,
183*c1733db1SRobert Mustacchi .dma_attr_flags = 0
184*c1733db1SRobert Mustacchi };
185*c1733db1SRobert Mustacchi
186*c1733db1SRobert Mustacchi static uint32_t
ismt_read32(ismt_t * ismt,uint32_t reg)187*c1733db1SRobert Mustacchi ismt_read32(ismt_t *ismt, uint32_t reg)
188*c1733db1SRobert Mustacchi {
189*c1733db1SRobert Mustacchi ASSERT3U(reg, <, ismt->ismt_regsize);
190*c1733db1SRobert Mustacchi return (ddi_get32(ismt->ismt_regs, (uint32_t *)(ismt->ismt_base +
191*c1733db1SRobert Mustacchi reg)));
192*c1733db1SRobert Mustacchi }
193*c1733db1SRobert Mustacchi
194*c1733db1SRobert Mustacchi static void
ismt_write32(ismt_t * ismt,uint32_t reg,uint32_t val)195*c1733db1SRobert Mustacchi ismt_write32(ismt_t *ismt, uint32_t reg, uint32_t val)
196*c1733db1SRobert Mustacchi {
197*c1733db1SRobert Mustacchi ASSERT3U(reg, <, ismt->ismt_regsize);
198*c1733db1SRobert Mustacchi ddi_put32(ismt->ismt_regs, (uint32_t *)(ismt->ismt_base + reg), val);
199*c1733db1SRobert Mustacchi }
200*c1733db1SRobert Mustacchi
201*c1733db1SRobert Mustacchi static void
ismt_write64(ismt_t * ismt,uint32_t reg,uint64_t val)202*c1733db1SRobert Mustacchi ismt_write64(ismt_t *ismt, uint32_t reg, uint64_t val)
203*c1733db1SRobert Mustacchi {
204*c1733db1SRobert Mustacchi ASSERT3U(reg, <, ismt->ismt_regsize);
205*c1733db1SRobert Mustacchi ddi_put64(ismt->ismt_regs, (uint64_t *)(ismt->ismt_base + reg), val);
206*c1733db1SRobert Mustacchi }
207*c1733db1SRobert Mustacchi
208*c1733db1SRobert Mustacchi static i2c_errno_t
ismt_prop_info(void * arg,i2c_prop_t prop,i2c_prop_info_t * info)209*c1733db1SRobert Mustacchi ismt_prop_info(void *arg, i2c_prop_t prop, i2c_prop_info_t *info)
210*c1733db1SRobert Mustacchi {
211*c1733db1SRobert Mustacchi switch (prop) {
212*c1733db1SRobert Mustacchi case I2C_PROP_BUS_SPEED:
213*c1733db1SRobert Mustacchi i2c_prop_info_set_pos_bit32(info, I2C_SPEED_STD |
214*c1733db1SRobert Mustacchi I2C_SPEED_FAST | I2C_SPEED_FPLUS);
215*c1733db1SRobert Mustacchi break;
216*c1733db1SRobert Mustacchi case SMBUS_PROP_SUP_OPS:
217*c1733db1SRobert Mustacchi case I2C_PROP_MAX_READ:
218*c1733db1SRobert Mustacchi case I2C_PROP_MAX_WRITE:
219*c1733db1SRobert Mustacchi case SMBUS_PROP_MAX_BLOCK:
220*c1733db1SRobert Mustacchi break;
221*c1733db1SRobert Mustacchi default:
222*c1733db1SRobert Mustacchi return (I2C_PROP_E_UNSUP);
223*c1733db1SRobert Mustacchi }
224*c1733db1SRobert Mustacchi
225*c1733db1SRobert Mustacchi i2c_prop_info_set_perm(info, I2C_PROP_PERM_RO);
226*c1733db1SRobert Mustacchi
227*c1733db1SRobert Mustacchi return (I2C_CORE_E_OK);
228*c1733db1SRobert Mustacchi }
229*c1733db1SRobert Mustacchi
230*c1733db1SRobert Mustacchi /*
231*c1733db1SRobert Mustacchi * Currently all the information that we return is static. If this changes, we
232*c1733db1SRobert Mustacchi * should ensure we hold ismt_mutex during this.
233*c1733db1SRobert Mustacchi */
234*c1733db1SRobert Mustacchi static i2c_errno_t
ismt_prop_get(void * arg,i2c_prop_t prop,void * buf,size_t buflen)235*c1733db1SRobert Mustacchi ismt_prop_get(void *arg, i2c_prop_t prop, void *buf, size_t buflen)
236*c1733db1SRobert Mustacchi {
237*c1733db1SRobert Mustacchi ismt_t *ismt = arg;
238*c1733db1SRobert Mustacchi uint32_t val;
239*c1733db1SRobert Mustacchi
240*c1733db1SRobert Mustacchi switch (prop) {
241*c1733db1SRobert Mustacchi case I2C_PROP_BUS_SPEED:
242*c1733db1SRobert Mustacchi val = ismt->ismt_speed;
243*c1733db1SRobert Mustacchi break;
244*c1733db1SRobert Mustacchi case SMBUS_PROP_SUP_OPS:
245*c1733db1SRobert Mustacchi val = SMBUS_PROP_OP_QUICK_COMMAND | SMBUS_PROP_OP_SEND_BYTE |
246*c1733db1SRobert Mustacchi SMBUS_PROP_OP_RECV_BYTE | SMBUS_PROP_OP_WRITE_BYTE |
247*c1733db1SRobert Mustacchi SMBUS_PROP_OP_READ_BYTE | SMBUS_PROP_OP_WRITE_WORD |
248*c1733db1SRobert Mustacchi SMBUS_PROP_OP_READ_WORD | SMBUS_PROP_OP_PROCESS_CALL |
249*c1733db1SRobert Mustacchi SMBUS_PROP_OP_WRITE_BLOCK | SMBUS_PROP_OP_READ_BLOCK |
250*c1733db1SRobert Mustacchi SMBUS_PROP_OP_BLOCK_PROCESS_CALL |
251*c1733db1SRobert Mustacchi SMBUS_PROP_OP_I2C_WRITE_BLOCK |
252*c1733db1SRobert Mustacchi SMBUS_PROP_OP_I2C_READ_BLOCK;
253*c1733db1SRobert Mustacchi break;
254*c1733db1SRobert Mustacchi case I2C_PROP_MAX_READ:
255*c1733db1SRobert Mustacchi case I2C_PROP_MAX_WRITE:
256*c1733db1SRobert Mustacchi val = ISMT_MAX_I2C;
257*c1733db1SRobert Mustacchi break;
258*c1733db1SRobert Mustacchi case SMBUS_PROP_MAX_BLOCK:
259*c1733db1SRobert Mustacchi val = ISMT_MAX_SMBUS;
260*c1733db1SRobert Mustacchi break;
261*c1733db1SRobert Mustacchi default:
262*c1733db1SRobert Mustacchi return (I2C_PROP_E_UNSUP);
263*c1733db1SRobert Mustacchi }
264*c1733db1SRobert Mustacchi
265*c1733db1SRobert Mustacchi VERIFY3U(buflen, >=, sizeof (val));
266*c1733db1SRobert Mustacchi bcopy(&val, buf, sizeof (val));
267*c1733db1SRobert Mustacchi return (I2C_CORE_E_OK);
268*c1733db1SRobert Mustacchi }
269*c1733db1SRobert Mustacchi
270*c1733db1SRobert Mustacchi static void
ismt_io_error(ismt_t * ismt,uint32_t sts)271*c1733db1SRobert Mustacchi ismt_io_error(ismt_t *ismt, uint32_t sts)
272*c1733db1SRobert Mustacchi {
273*c1733db1SRobert Mustacchi i2c_ctrl_error_t err;
274*c1733db1SRobert Mustacchi VERIFY3P(ismt->ismt_err, !=, NULL);
275*c1733db1SRobert Mustacchi
276*c1733db1SRobert Mustacchi if (ISMT_DESC_STS_GET_NACK(sts) != 0) {
277*c1733db1SRobert Mustacchi if (ISMT_DESC_STS_GET_WRLEN(sts) == 0) {
278*c1733db1SRobert Mustacchi err = I2C_CTRL_E_ADDR_NACK;
279*c1733db1SRobert Mustacchi } else {
280*c1733db1SRobert Mustacchi err = I2C_CTRL_E_DATA_NACK;
281*c1733db1SRobert Mustacchi }
282*c1733db1SRobert Mustacchi } else if (ISMT_DESC_STS_GET_CRC(sts) != 0) {
283*c1733db1SRobert Mustacchi /*
284*c1733db1SRobert Mustacchi * As we don't enable PEC right now, we don't expect to see
285*c1733db1SRobert Mustacchi * this. When we do, then this should be changed.
286*c1733db1SRobert Mustacchi */
287*c1733db1SRobert Mustacchi err = I2C_CTRL_E_DRIVER;
288*c1733db1SRobert Mustacchi } else if (ISMT_DESC_STS_GET_CLTO(sts) != 0) {
289*c1733db1SRobert Mustacchi err = I2C_CTRL_E_SMBUS_CLOCK_LOW;
290*c1733db1SRobert Mustacchi } else if (ISMT_DESC_STS_GET_COL(sts) != 0) {
291*c1733db1SRobert Mustacchi err = I2C_CTRL_E_ARB_LOST;
292*c1733db1SRobert Mustacchi } else if (ISMT_DESC_STS_GET_LPR(sts) != 0) {
293*c1733db1SRobert Mustacchi err = I2C_CTRL_E_DRIVER;
294*c1733db1SRobert Mustacchi } else {
295*c1733db1SRobert Mustacchi err = I2C_CTRL_E_INTERNAL;
296*c1733db1SRobert Mustacchi }
297*c1733db1SRobert Mustacchi
298*c1733db1SRobert Mustacchi i2c_ctrl_io_error(ismt->ismt_err, I2C_CORE_E_CONTROLLER, err);
299*c1733db1SRobert Mustacchi }
300*c1733db1SRobert Mustacchi
301*c1733db1SRobert Mustacchi /*
302*c1733db1SRobert Mustacchi * Process the current completion.
303*c1733db1SRobert Mustacchi */
304*c1733db1SRobert Mustacchi static void
ismt_io(ismt_t * ismt)305*c1733db1SRobert Mustacchi ismt_io(ismt_t *ismt)
306*c1733db1SRobert Mustacchi {
307*c1733db1SRobert Mustacchi ismt_desc_t *desc;
308*c1733db1SRobert Mustacchi uint32_t sts;
309*c1733db1SRobert Mustacchi
310*c1733db1SRobert Mustacchi VERIFY(MUTEX_HELD(&ismt->ismt_mutex));
311*c1733db1SRobert Mustacchi ISMT_DMA_SYNC(ismt->ismt_ring_dma, DDI_DMA_SYNC_FORKERNEL);
312*c1733db1SRobert Mustacchi ISMT_DMA_SYNC(ismt->ismt_data_dma, DDI_DMA_SYNC_FORKERNEL);
313*c1733db1SRobert Mustacchi desc = &ismt->ismt_ring[ismt->ismt_tail];
314*c1733db1SRobert Mustacchi ismt->ismt_tail = (ismt->ismt_tail + 1) % ISMT_RING_NENTS;
315*c1733db1SRobert Mustacchi const uint8_t *buf = (uint8_t *)ismt->ismt_data_dma.id_va;
316*c1733db1SRobert Mustacchi
317*c1733db1SRobert Mustacchi sts = LE_32(desc->id_status);
318*c1733db1SRobert Mustacchi if (ISMT_DESC_STS_GET_SCS(sts) == 0) {
319*c1733db1SRobert Mustacchi ismt_io_error(ismt, sts);
320*c1733db1SRobert Mustacchi return;
321*c1733db1SRobert Mustacchi }
322*c1733db1SRobert Mustacchi
323*c1733db1SRobert Mustacchi if (ismt->ismt_i2creq != NULL) {
324*c1733db1SRobert Mustacchi VERIFY3P(ismt->ismt_req, ==, NULL);
325*c1733db1SRobert Mustacchi if (ismt->ismt_i2creq->ir_rlen > 0) {
326*c1733db1SRobert Mustacchi VERIFY3U(ismt->ismt_i2creq->ir_rlen, ==,
327*c1733db1SRobert Mustacchi ISMT_DESC_STS_GET_RDLEN(sts));
328*c1733db1SRobert Mustacchi bcopy(buf, ismt->ismt_i2creq->ir_rdata,
329*c1733db1SRobert Mustacchi ISMT_DESC_STS_GET_RDLEN(sts));
330*c1733db1SRobert Mustacchi }
331*c1733db1SRobert Mustacchi i2c_ctrl_io_success(ismt->ismt_err);
332*c1733db1SRobert Mustacchi return;
333*c1733db1SRobert Mustacchi }
334*c1733db1SRobert Mustacchi
335*c1733db1SRobert Mustacchi switch (ismt->ismt_req->smbr_op) {
336*c1733db1SRobert Mustacchi case SMBUS_OP_QUICK_COMMAND:
337*c1733db1SRobert Mustacchi case SMBUS_OP_SEND_BYTE:
338*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BYTE:
339*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_WORD:
340*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BLOCK:
341*c1733db1SRobert Mustacchi case SMBUS_OP_I2C_WRITE_BLOCK:
342*c1733db1SRobert Mustacchi /*
343*c1733db1SRobert Mustacchi * Nothing to do for writes.
344*c1733db1SRobert Mustacchi */
345*c1733db1SRobert Mustacchi break;
346*c1733db1SRobert Mustacchi case SMBUS_OP_RECV_BYTE:
347*c1733db1SRobert Mustacchi case SMBUS_OP_READ_BYTE:
348*c1733db1SRobert Mustacchi ismt->ismt_req->smbr_rdata[0] = buf[0];
349*c1733db1SRobert Mustacchi break;
350*c1733db1SRobert Mustacchi case SMBUS_OP_READ_WORD:
351*c1733db1SRobert Mustacchi case SMBUS_OP_PROCESS_CALL:
352*c1733db1SRobert Mustacchi ismt->ismt_req->smbr_rdata[0] = buf[0];
353*c1733db1SRobert Mustacchi ismt->ismt_req->smbr_rdata[1] = buf[1];
354*c1733db1SRobert Mustacchi break;
355*c1733db1SRobert Mustacchi case SMBUS_OP_READ_BLOCK:
356*c1733db1SRobert Mustacchi case SMBUS_OP_BLOCK_PROCESS_CALL:
357*c1733db1SRobert Mustacchi if (ISMT_DESC_STS_GET_RDLEN(sts) != buf[0] + 1) {
358*c1733db1SRobert Mustacchi i2c_ctrl_io_error(ismt->ismt_err, I2C_CORE_E_CONTROLLER,
359*c1733db1SRobert Mustacchi I2C_CTRL_E_DRIVER);
360*c1733db1SRobert Mustacchi return;
361*c1733db1SRobert Mustacchi }
362*c1733db1SRobert Mustacchi ismt->ismt_req->smbr_rlen = buf[0];
363*c1733db1SRobert Mustacchi bcopy(&buf[1], ismt->ismt_req->smbr_rdata, buf[0]);
364*c1733db1SRobert Mustacchi break;
365*c1733db1SRobert Mustacchi case SMBUS_OP_I2C_READ_BLOCK:
366*c1733db1SRobert Mustacchi bcopy(buf, ismt->ismt_req->smbr_rdata,
367*c1733db1SRobert Mustacchi ISMT_DESC_STS_GET_RDLEN(sts));
368*c1733db1SRobert Mustacchi break;
369*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U32:
370*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U64:
371*c1733db1SRobert Mustacchi case SMBUS_OP_READ_U32:
372*c1733db1SRobert Mustacchi case SMBUS_OP_READ_U64:
373*c1733db1SRobert Mustacchi case SMBUS_OP_HOST_NOTIFY:
374*c1733db1SRobert Mustacchi default:
375*c1733db1SRobert Mustacchi panic("programmer error: unsupported request type 0x%x should "
376*c1733db1SRobert Mustacchi "not have been completed", ismt->ismt_req->smbr_op);
377*c1733db1SRobert Mustacchi }
378*c1733db1SRobert Mustacchi
379*c1733db1SRobert Mustacchi i2c_ctrl_io_success(ismt->ismt_err);
380*c1733db1SRobert Mustacchi }
381*c1733db1SRobert Mustacchi
382*c1733db1SRobert Mustacchi /*
383*c1733db1SRobert Mustacchi * When we're using MSI interrupts then the hardware will automatically clear
384*c1733db1SRobert Mustacchi * the controller's interrupt status register based on our configuration.
385*c1733db1SRobert Mustacchi * However if we're using INTx, then we will need to take care of reading the
386*c1733db1SRobert Mustacchi * various status registers and checking what has happened.
387*c1733db1SRobert Mustacchi *
388*c1733db1SRobert Mustacchi * One nice thing is that we'll otherwise always get an interrupt when the whole
389*c1733db1SRobert Mustacchi * operation is done.
390*c1733db1SRobert Mustacchi */
391*c1733db1SRobert Mustacchi static uint_t
ismt_intr(caddr_t arg1,caddr_t arg2)392*c1733db1SRobert Mustacchi ismt_intr(caddr_t arg1, caddr_t arg2)
393*c1733db1SRobert Mustacchi {
394*c1733db1SRobert Mustacchi ismt_t *ismt = (ismt_t *)arg1;
395*c1733db1SRobert Mustacchi uint32_t msts;
396*c1733db1SRobert Mustacchi bool mis, meis;
397*c1733db1SRobert Mustacchi
398*c1733db1SRobert Mustacchi mutex_enter(&ismt->ismt_mutex);
399*c1733db1SRobert Mustacchi if (ismt->ismt_itype == DDI_INTR_TYPE_FIXED) {
400*c1733db1SRobert Mustacchi msts = ismt_read32(ismt, ISMT_R_MSTS);
401*c1733db1SRobert Mustacchi mis = ISMT_R_MSTS_GET_MIS(msts);
402*c1733db1SRobert Mustacchi meis = ISMT_R_MSTS_GET_MEIS(msts);
403*c1733db1SRobert Mustacchi
404*c1733db1SRobert Mustacchi if (!mis && !meis) {
405*c1733db1SRobert Mustacchi mutex_exit(&ismt->ismt_mutex);
406*c1733db1SRobert Mustacchi return (DDI_INTR_UNCLAIMED);
407*c1733db1SRobert Mustacchi }
408*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_MSTS, msts);
409*c1733db1SRobert Mustacchi }
410*c1733db1SRobert Mustacchi
411*c1733db1SRobert Mustacchi ismt_io(ismt);
412*c1733db1SRobert Mustacchi ismt->ismt_req_done = true;
413*c1733db1SRobert Mustacchi cv_signal(&ismt->ismt_cv);
414*c1733db1SRobert Mustacchi mutex_exit(&ismt->ismt_mutex);
415*c1733db1SRobert Mustacchi return (DDI_INTR_CLAIMED);
416*c1733db1SRobert Mustacchi }
417*c1733db1SRobert Mustacchi
418*c1733db1SRobert Mustacchi static void
ismt_wait(ismt_t * ismt)419*c1733db1SRobert Mustacchi ismt_wait(ismt_t *ismt)
420*c1733db1SRobert Mustacchi {
421*c1733db1SRobert Mustacchi VERIFY(MUTEX_HELD(&ismt->ismt_mutex));
422*c1733db1SRobert Mustacchi VERIFY(ismt->ismt_req == NULL || ismt->ismt_i2creq == NULL);
423*c1733db1SRobert Mustacchi VERIFY3P(ismt->ismt_req, !=, ismt->ismt_i2creq);
424*c1733db1SRobert Mustacchi
425*c1733db1SRobert Mustacchi uint32_t to = i2c_ctrl_timeout_delay_us(ismt->ismt_hdl, I2C_CTRL_TO_IO);
426*c1733db1SRobert Mustacchi clock_t abs = ddi_get_lbolt() + drv_usectohz(to);
427*c1733db1SRobert Mustacchi while (!ismt->ismt_req_done) {
428*c1733db1SRobert Mustacchi clock_t ret = cv_timedwait(&ismt->ismt_cv, &ismt->ismt_mutex,
429*c1733db1SRobert Mustacchi abs);
430*c1733db1SRobert Mustacchi if (ret == -1) {
431*c1733db1SRobert Mustacchi break;
432*c1733db1SRobert Mustacchi }
433*c1733db1SRobert Mustacchi }
434*c1733db1SRobert Mustacchi
435*c1733db1SRobert Mustacchi /*
436*c1733db1SRobert Mustacchi * The command timed out. We need to set the KILL bit, complete the
437*c1733db1SRobert Mustacchi * transaction, and go from there.
438*c1733db1SRobert Mustacchi */
439*c1733db1SRobert Mustacchi if (!ismt->ismt_req_done) {
440*c1733db1SRobert Mustacchi uint32_t val = ISMT_R_GCTRL_SET_KILL(0, 1);
441*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_GCTRL, val);
442*c1733db1SRobert Mustacchi i2c_ctrl_io_error(ismt->ismt_err, I2C_CORE_E_CONTROLLER,
443*c1733db1SRobert Mustacchi I2C_CTRL_E_REQ_TO);
444*c1733db1SRobert Mustacchi ismt->ismt_req_done = true;
445*c1733db1SRobert Mustacchi }
446*c1733db1SRobert Mustacchi }
447*c1733db1SRobert Mustacchi
448*c1733db1SRobert Mustacchi static void
ismt_io_reset(ismt_t * ismt)449*c1733db1SRobert Mustacchi ismt_io_reset(ismt_t *ismt)
450*c1733db1SRobert Mustacchi {
451*c1733db1SRobert Mustacchi VERIFY(MUTEX_HELD(&ismt->ismt_mutex));
452*c1733db1SRobert Mustacchi bzero(ismt->ismt_data_dma.id_va, ISMT_DATA_BUF_SIZE);
453*c1733db1SRobert Mustacchi bzero(ismt->ismt_icl_dma.id_va, ISMT_ICL_DMA_SIZE);
454*c1733db1SRobert Mustacchi ismt->ismt_req = NULL;
455*c1733db1SRobert Mustacchi ismt->ismt_i2creq = NULL;
456*c1733db1SRobert Mustacchi ismt->ismt_err = NULL;
457*c1733db1SRobert Mustacchi ismt->ismt_req_done = false;
458*c1733db1SRobert Mustacchi }
459*c1733db1SRobert Mustacchi
460*c1733db1SRobert Mustacchi /*
461*c1733db1SRobert Mustacchi * Set the things that are common across all I/O requests: the address, the
462*c1733db1SRobert Mustacchi * request for the fair bit, and ask for an interrupt. Hardware will ownly honor
463*c1733db1SRobert Mustacchi * the bit if we're using MSIs and otherwise will always inject the interrupt,
464*c1733db1SRobert Mustacchi * so we set this regardless.
465*c1733db1SRobert Mustacchi */
466*c1733db1SRobert Mustacchi static void
ismt_io_cmd_init(const i2c_addr_t * addr,uint32_t * cmdp)467*c1733db1SRobert Mustacchi ismt_io_cmd_init(const i2c_addr_t *addr, uint32_t *cmdp)
468*c1733db1SRobert Mustacchi {
469*c1733db1SRobert Mustacchi uint32_t cmd;
470*c1733db1SRobert Mustacchi
471*c1733db1SRobert Mustacchi ASSERT3U(addr->ia_type, ==, I2C_ADDR_7BIT);
472*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_ADDR(0, addr->ia_addr);
473*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_INT(cmd, 1);
474*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_FAIR(cmd, 1);
475*c1733db1SRobert Mustacchi *cmdp = cmd;
476*c1733db1SRobert Mustacchi }
477*c1733db1SRobert Mustacchi
478*c1733db1SRobert Mustacchi static void
ismt_io_cmd_submit(ismt_t * ismt,uint32_t cmd,bool data)479*c1733db1SRobert Mustacchi ismt_io_cmd_submit(ismt_t *ismt, uint32_t cmd, bool data)
480*c1733db1SRobert Mustacchi {
481*c1733db1SRobert Mustacchi ismt_desc_t *desc;
482*c1733db1SRobert Mustacchi
483*c1733db1SRobert Mustacchi VERIFY(MUTEX_HELD(&ismt->ismt_mutex));
484*c1733db1SRobert Mustacchi desc = &ismt->ismt_ring[ismt->ismt_head];
485*c1733db1SRobert Mustacchi bzero(desc, sizeof (desc));
486*c1733db1SRobert Mustacchi
487*c1733db1SRobert Mustacchi desc->id_cmd_addr = LE_32(cmd);
488*c1733db1SRobert Mustacchi if (data) {
489*c1733db1SRobert Mustacchi const ddi_dma_cookie_t *c;
490*c1733db1SRobert Mustacchi
491*c1733db1SRobert Mustacchi c = ddi_dma_cookie_one(ismt->ismt_data_dma.id_hdl);
492*c1733db1SRobert Mustacchi desc->id_low = LE_32(bitx64(c->dmac_laddress, 31, 0));
493*c1733db1SRobert Mustacchi desc->id_high = LE_32(bitx64(c->dmac_laddress, 63, 32));
494*c1733db1SRobert Mustacchi ISMT_DMA_SYNC(ismt->ismt_data_dma, DDI_DMA_SYNC_FORDEV);
495*c1733db1SRobert Mustacchi }
496*c1733db1SRobert Mustacchi ISMT_DMA_SYNC(ismt->ismt_ring_dma, DDI_DMA_SYNC_FORDEV);
497*c1733db1SRobert Mustacchi
498*c1733db1SRobert Mustacchi /*
499*c1733db1SRobert Mustacchi * Proceed to tell hardware to process this command. The datasheet
500*c1733db1SRobert Mustacchi * suggests we need to update the descriptor pointer and then come back
501*c1733db1SRobert Mustacchi * and ask the hardware to start as we can't set SS until at least one
502*c1733db1SRobert Mustacchi * descriptor has been programmed.
503*c1733db1SRobert Mustacchi */
504*c1733db1SRobert Mustacchi ismt->ismt_head = (ismt->ismt_head + 1) % ISMT_RING_NENTS;
505*c1733db1SRobert Mustacchi uint32_t mctrl = ismt_read32(ismt, ISMT_R_MCTRL);
506*c1733db1SRobert Mustacchi mctrl = ISMT_R_MCTRL_SET_FMHP(mctrl, ismt->ismt_head);
507*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_MCTRL, mctrl);
508*c1733db1SRobert Mustacchi mctrl = ISMT_R_MCTRL_SET_SS(mctrl, 1);
509*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_MCTRL, mctrl);
510*c1733db1SRobert Mustacchi
511*c1733db1SRobert Mustacchi /*
512*c1733db1SRobert Mustacchi * The command is running. We now need to wait for an interrupt or poll
513*c1733db1SRobert Mustacchi * for completion. Unlike other drivers, we always have the interrupt
514*c1733db1SRobert Mustacchi * enabled, which means we can't really poll.
515*c1733db1SRobert Mustacchi */
516*c1733db1SRobert Mustacchi ismt_wait(ismt);
517*c1733db1SRobert Mustacchi }
518*c1733db1SRobert Mustacchi
519*c1733db1SRobert Mustacchi static void
ismt_io_smbus(void * arg,uint32_t port,smbus_req_t * req)520*c1733db1SRobert Mustacchi ismt_io_smbus(void *arg, uint32_t port, smbus_req_t *req)
521*c1733db1SRobert Mustacchi {
522*c1733db1SRobert Mustacchi ismt_t *ismt = arg;
523*c1733db1SRobert Mustacchi uint8_t *buf;
524*c1733db1SRobert Mustacchi uint32_t cmd;
525*c1733db1SRobert Mustacchi bool data = false;
526*c1733db1SRobert Mustacchi
527*c1733db1SRobert Mustacchi mutex_enter(&ismt->ismt_mutex);
528*c1733db1SRobert Mustacchi ismt_io_reset(ismt);
529*c1733db1SRobert Mustacchi ismt->ismt_req = req;
530*c1733db1SRobert Mustacchi ismt->ismt_err = &req->smbr_error;
531*c1733db1SRobert Mustacchi
532*c1733db1SRobert Mustacchi buf = (uint8_t *)ismt->ismt_data_dma.id_va;
533*c1733db1SRobert Mustacchi
534*c1733db1SRobert Mustacchi /*
535*c1733db1SRobert Mustacchi * Set up the descriptor. In particualr we need to determine whether to
536*c1733db1SRobert Mustacchi * set:
537*c1733db1SRobert Mustacchi *
538*c1733db1SRobert Mustacchi * - The read address bit (default is write)
539*c1733db1SRobert Mustacchi * - The block request bit
540*c1733db1SRobert Mustacchi * - The C/WRL bit which determines whether the write field is the
541*c1733db1SRobert Mustacchi * command.
542*c1733db1SRobert Mustacchi * - The read and write length, if non-zero.
543*c1733db1SRobert Mustacchi * - Whether we are going to use the data pointer and if we need to do
544*c1733db1SRobert Mustacchi * anyhting to get it ready
545*c1733db1SRobert Mustacchi *
546*c1733db1SRobert Mustacchi * The read address bit and whether we use data are bools that we apply
547*c1733db1SRobert Mustacchi * at the end.
548*c1733db1SRobert Mustacchi */
549*c1733db1SRobert Mustacchi ismt_io_cmd_init(&req->smbr_addr, &cmd);
550*c1733db1SRobert Mustacchi switch (req->smbr_op) {
551*c1733db1SRobert Mustacchi case SMBUS_OP_QUICK_COMMAND:
552*c1733db1SRobert Mustacchi if ((req->smbr_flags & I2C_IO_REQ_F_QUICK_WRITE) != 0) {
553*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
554*c1733db1SRobert Mustacchi } else {
555*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
556*c1733db1SRobert Mustacchi }
557*c1733db1SRobert Mustacchi break;
558*c1733db1SRobert Mustacchi case SMBUS_OP_SEND_BYTE:
559*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
560*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_CWRL(cmd, 1);
561*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_wdata[0]);
562*c1733db1SRobert Mustacchi break;
563*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BYTE:
564*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
565*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, 2);
566*c1733db1SRobert Mustacchi data = true;
567*c1733db1SRobert Mustacchi buf[0] = req->smbr_cmd;
568*c1733db1SRobert Mustacchi buf[1] = req->smbr_wdata[0];
569*c1733db1SRobert Mustacchi break;
570*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_WORD:
571*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
572*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, 3);
573*c1733db1SRobert Mustacchi data = true;
574*c1733db1SRobert Mustacchi buf[0] = req->smbr_cmd;
575*c1733db1SRobert Mustacchi buf[1] = req->smbr_wdata[0];
576*c1733db1SRobert Mustacchi buf[2] = req->smbr_wdata[1];
577*c1733db1SRobert Mustacchi break;
578*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_BLOCK:
579*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
580*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_BLK(cmd, 1);
581*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_wlen, <=, ISMT_MAX_SMBUS);
582*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_wlen + 1);
583*c1733db1SRobert Mustacchi data = true;
584*c1733db1SRobert Mustacchi buf[0] = req->smbr_cmd;
585*c1733db1SRobert Mustacchi bcopy(req->smbr_wdata, &buf[1], req->smbr_wlen);
586*c1733db1SRobert Mustacchi break;
587*c1733db1SRobert Mustacchi case SMBUS_OP_I2C_WRITE_BLOCK:
588*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
589*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_I2C(cmd, 1);
590*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_wlen, >, 0);
591*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_wlen, <=, ISMT_MAX_I2C);
592*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_wlen + 1);
593*c1733db1SRobert Mustacchi data = true;
594*c1733db1SRobert Mustacchi buf[0] = req->smbr_cmd;
595*c1733db1SRobert Mustacchi bcopy(req->smbr_wdata, &buf[1], req->smbr_wlen);
596*c1733db1SRobert Mustacchi break;
597*c1733db1SRobert Mustacchi case SMBUS_OP_RECV_BYTE:
598*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
599*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, 1);
600*c1733db1SRobert Mustacchi data = true;
601*c1733db1SRobert Mustacchi break;
602*c1733db1SRobert Mustacchi case SMBUS_OP_READ_BYTE:
603*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
604*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_CWRL(cmd, 1);
605*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_cmd);
606*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, 1);
607*c1733db1SRobert Mustacchi data = true;
608*c1733db1SRobert Mustacchi break;
609*c1733db1SRobert Mustacchi case SMBUS_OP_READ_WORD:
610*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
611*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_CWRL(cmd, 1);
612*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_cmd);
613*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, 2);
614*c1733db1SRobert Mustacchi data = true;
615*c1733db1SRobert Mustacchi break;
616*c1733db1SRobert Mustacchi case SMBUS_OP_READ_BLOCK:
617*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
618*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_BLK(cmd, 1);
619*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_CWRL(cmd, 1);
620*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_cmd);
621*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_rlen, >, 0);
622*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_rlen, <=, ISMT_MAX_SMBUS);
623*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, req->smbr_rlen + 1);
624*c1733db1SRobert Mustacchi data = true;
625*c1733db1SRobert Mustacchi break;
626*c1733db1SRobert Mustacchi case SMBUS_OP_I2C_READ_BLOCK:
627*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
628*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_I2C(cmd, 1);
629*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_CWRL(cmd, 1);
630*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_cmd);
631*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_rlen, >, 0);
632*c1733db1SRobert Mustacchi VERIFY3U(req->smbr_rlen, <=, ISMT_MAX_I2C);
633*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, req->smbr_rlen);
634*c1733db1SRobert Mustacchi data = true;
635*c1733db1SRobert Mustacchi break;
636*c1733db1SRobert Mustacchi case SMBUS_OP_PROCESS_CALL:
637*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
638*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, 3);
639*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, 2);
640*c1733db1SRobert Mustacchi data = true;
641*c1733db1SRobert Mustacchi buf[0] = req->smbr_cmd;
642*c1733db1SRobert Mustacchi buf[1] = req->smbr_wdata[0];
643*c1733db1SRobert Mustacchi buf[2] = req->smbr_wdata[1];
644*c1733db1SRobert Mustacchi break;
645*c1733db1SRobert Mustacchi case SMBUS_OP_BLOCK_PROCESS_CALL:
646*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
647*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->smbr_wlen + 1);
648*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, req->smbr_rlen + 1);
649*c1733db1SRobert Mustacchi data = true;
650*c1733db1SRobert Mustacchi buf[0] = req->smbr_cmd;
651*c1733db1SRobert Mustacchi bcopy(req->smbr_wdata, &buf[1], req->smbr_wlen);
652*c1733db1SRobert Mustacchi break;
653*c1733db1SRobert Mustacchi /*
654*c1733db1SRobert Mustacchi * As the datasheets don't have a way to directly run the U32/U64
655*c1733db1SRobert Mustacchi * operations, we allow our translation layer to take care of it.
656*c1733db1SRobert Mustacchi */
657*c1733db1SRobert Mustacchi case SMBUS_OP_READ_U32:
658*c1733db1SRobert Mustacchi case SMBUS_OP_READ_U64:
659*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U32:
660*c1733db1SRobert Mustacchi case SMBUS_OP_WRITE_U64:
661*c1733db1SRobert Mustacchi case SMBUS_OP_HOST_NOTIFY:
662*c1733db1SRobert Mustacchi default:
663*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "!framework passed "
664*c1733db1SRobert Mustacchi "unsupported SMBus command 0x%x", req->smbr_op);
665*c1733db1SRobert Mustacchi i2c_ctrl_io_error(&req->smbr_error, I2C_CORE_E_CONTROLLER,
666*c1733db1SRobert Mustacchi I2C_CTRL_E_UNSUP_CMD);
667*c1733db1SRobert Mustacchi goto done;
668*c1733db1SRobert Mustacchi
669*c1733db1SRobert Mustacchi }
670*c1733db1SRobert Mustacchi
671*c1733db1SRobert Mustacchi ismt_io_cmd_submit(ismt, cmd, data);
672*c1733db1SRobert Mustacchi done:
673*c1733db1SRobert Mustacchi ismt->ismt_req = NULL;
674*c1733db1SRobert Mustacchi ismt->ismt_i2creq = NULL;
675*c1733db1SRobert Mustacchi ismt->ismt_err = NULL;
676*c1733db1SRobert Mustacchi ismt->ismt_req_done = false;
677*c1733db1SRobert Mustacchi mutex_exit(&ismt->ismt_mutex);
678*c1733db1SRobert Mustacchi }
679*c1733db1SRobert Mustacchi
680*c1733db1SRobert Mustacchi static void
ismt_io_i2c(void * arg,uint32_t port,i2c_req_t * req)681*c1733db1SRobert Mustacchi ismt_io_i2c(void *arg, uint32_t port, i2c_req_t *req)
682*c1733db1SRobert Mustacchi {
683*c1733db1SRobert Mustacchi ismt_t *ismt = arg;
684*c1733db1SRobert Mustacchi uint8_t *buf;
685*c1733db1SRobert Mustacchi uint32_t cmd;
686*c1733db1SRobert Mustacchi bool data = false;
687*c1733db1SRobert Mustacchi
688*c1733db1SRobert Mustacchi mutex_enter(&ismt->ismt_mutex);
689*c1733db1SRobert Mustacchi ismt_io_reset(ismt);
690*c1733db1SRobert Mustacchi ismt->ismt_i2creq = req;
691*c1733db1SRobert Mustacchi ismt->ismt_err = &req->ir_error;
692*c1733db1SRobert Mustacchi
693*c1733db1SRobert Mustacchi buf = (uint8_t *)ismt->ismt_data_dma.id_va;
694*c1733db1SRobert Mustacchi
695*c1733db1SRobert Mustacchi /*
696*c1733db1SRobert Mustacchi * I2C Commands are required to always set the I2C bit and we must never
697*c1733db1SRobert Mustacchi * set the block bit. The remaining flags and set up depend on whether
698*c1733db1SRobert Mustacchi * we're doing a write, a read, or a write folowed by a read.
699*c1733db1SRobert Mustacchi */
700*c1733db1SRobert Mustacchi ismt_io_cmd_init(&req->ir_addr, &cmd);
701*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_I2C(cmd, 1);
702*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_BLK(cmd, 0);
703*c1733db1SRobert Mustacchi
704*c1733db1SRobert Mustacchi if (req->ir_rlen > 0) {
705*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_READ);
706*c1733db1SRobert Mustacchi VERIFY3U(req->ir_rlen, <, ISMT_MAX_I2C);
707*c1733db1SRobert Mustacchi data = true;
708*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RDLEN(cmd, req->ir_rlen);
709*c1733db1SRobert Mustacchi }
710*c1733db1SRobert Mustacchi
711*c1733db1SRobert Mustacchi if (req->ir_wlen > 0) {
712*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_RW(cmd, ISMT_DESC_CMD_RW_WRITE);
713*c1733db1SRobert Mustacchi
714*c1733db1SRobert Mustacchi /*
715*c1733db1SRobert Mustacchi * The datasheet tells us that if we have a 1 byte write, we
716*c1733db1SRobert Mustacchi * need to set C/WRL and it will encode the data byte. Otherwise
717*c1733db1SRobert Mustacchi * we use the normal write length.
718*c1733db1SRobert Mustacchi */
719*c1733db1SRobert Mustacchi if (req->ir_wlen == 1) {
720*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_CWRL(cmd, 1);
721*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->ir_wdata[0]);
722*c1733db1SRobert Mustacchi } else {
723*c1733db1SRobert Mustacchi VERIFY3U(req->ir_wlen, >, 0);
724*c1733db1SRobert Mustacchi VERIFY3U(req->ir_wlen, <, ISMT_MAX_I2C);
725*c1733db1SRobert Mustacchi cmd = ISMT_DESC_CMD_SET_WRLEN(cmd, req->ir_wlen);
726*c1733db1SRobert Mustacchi data = true;
727*c1733db1SRobert Mustacchi bcopy(req->ir_wdata, buf, req->ir_wlen);
728*c1733db1SRobert Mustacchi }
729*c1733db1SRobert Mustacchi }
730*c1733db1SRobert Mustacchi
731*c1733db1SRobert Mustacchi ismt_io_cmd_submit(ismt, cmd, data);
732*c1733db1SRobert Mustacchi
733*c1733db1SRobert Mustacchi ismt->ismt_req = NULL;
734*c1733db1SRobert Mustacchi ismt->ismt_i2creq = NULL;
735*c1733db1SRobert Mustacchi ismt->ismt_req_done = false;
736*c1733db1SRobert Mustacchi mutex_exit(&ismt->ismt_mutex);
737*c1733db1SRobert Mustacchi }
738*c1733db1SRobert Mustacchi
739*c1733db1SRobert Mustacchi static const i2c_ctrl_ops_t ismt_ctrl_ops = {
740*c1733db1SRobert Mustacchi .i2c_port_name_f = i2c_ctrl_port_name_portno,
741*c1733db1SRobert Mustacchi .i2c_io_i2c_f = ismt_io_i2c,
742*c1733db1SRobert Mustacchi .i2c_io_smbus_f = ismt_io_smbus,
743*c1733db1SRobert Mustacchi .i2c_prop_info_f = ismt_prop_info,
744*c1733db1SRobert Mustacchi .i2c_prop_get_f = ismt_prop_get
745*c1733db1SRobert Mustacchi };
746*c1733db1SRobert Mustacchi
747*c1733db1SRobert Mustacchi static bool
ismt_setup_regs(ismt_t * ismt)748*c1733db1SRobert Mustacchi ismt_setup_regs(ismt_t *ismt)
749*c1733db1SRobert Mustacchi {
750*c1733db1SRobert Mustacchi int ret;
751*c1733db1SRobert Mustacchi ddi_device_acc_attr_t attr;
752*c1733db1SRobert Mustacchi
753*c1733db1SRobert Mustacchi bzero(&attr, sizeof (attr));
754*c1733db1SRobert Mustacchi attr.devacc_attr_version = DDI_DEVICE_ATTR_V1;
755*c1733db1SRobert Mustacchi attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
756*c1733db1SRobert Mustacchi attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
757*c1733db1SRobert Mustacchi attr.devacc_attr_access = DDI_DEFAULT_ACC;
758*c1733db1SRobert Mustacchi
759*c1733db1SRobert Mustacchi if (ddi_dev_regsize(ismt->ismt_dip, ISMT_REGNO, &ismt->ismt_regsize) !=
760*c1733db1SRobert Mustacchi DDI_SUCCESS) {
761*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to get regs[%u] size",
762*c1733db1SRobert Mustacchi ISMT_REGNO);
763*c1733db1SRobert Mustacchi return (false);
764*c1733db1SRobert Mustacchi }
765*c1733db1SRobert Mustacchi
766*c1733db1SRobert Mustacchi ret = ddi_regs_map_setup(ismt->ismt_dip, ISMT_REGNO, &ismt->ismt_base,
767*c1733db1SRobert Mustacchi 0, ismt->ismt_regsize, &attr, &ismt->ismt_regs);
768*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
769*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to map regs[%u]: %u",
770*c1733db1SRobert Mustacchi ISMT_REGNO, ret);
771*c1733db1SRobert Mustacchi return (false);
772*c1733db1SRobert Mustacchi }
773*c1733db1SRobert Mustacchi
774*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_REGS;
775*c1733db1SRobert Mustacchi return (true);
776*c1733db1SRobert Mustacchi }
777*c1733db1SRobert Mustacchi
778*c1733db1SRobert Mustacchi static void
ismt_dma_free(ismt_dma_t * dma)779*c1733db1SRobert Mustacchi ismt_dma_free(ismt_dma_t *dma)
780*c1733db1SRobert Mustacchi {
781*c1733db1SRobert Mustacchi /* Proxy for DMA handle bound */
782*c1733db1SRobert Mustacchi if (dma->id_size != 0) {
783*c1733db1SRobert Mustacchi (void) ddi_dma_unbind_handle(dma->id_hdl);
784*c1733db1SRobert Mustacchi dma->id_size = 0;
785*c1733db1SRobert Mustacchi }
786*c1733db1SRobert Mustacchi
787*c1733db1SRobert Mustacchi if (dma->id_acc != NULL) {
788*c1733db1SRobert Mustacchi ddi_dma_mem_free(&dma->id_acc);
789*c1733db1SRobert Mustacchi dma->id_acc = NULL;
790*c1733db1SRobert Mustacchi dma->id_va = NULL;
791*c1733db1SRobert Mustacchi dma->id_alloc_len = 0;
792*c1733db1SRobert Mustacchi }
793*c1733db1SRobert Mustacchi
794*c1733db1SRobert Mustacchi if (dma->id_hdl != NULL) {
795*c1733db1SRobert Mustacchi ddi_dma_free_handle(&dma->id_hdl);
796*c1733db1SRobert Mustacchi dma->id_hdl = NULL;
797*c1733db1SRobert Mustacchi }
798*c1733db1SRobert Mustacchi
799*c1733db1SRobert Mustacchi ASSERT0(dma->id_size);
800*c1733db1SRobert Mustacchi ASSERT0(dma->id_alloc_len);
801*c1733db1SRobert Mustacchi ASSERT3P(dma->id_acc, ==, NULL);
802*c1733db1SRobert Mustacchi ASSERT3P(dma->id_hdl, ==, NULL);
803*c1733db1SRobert Mustacchi ASSERT3P(dma->id_va, ==, NULL);
804*c1733db1SRobert Mustacchi }
805*c1733db1SRobert Mustacchi
806*c1733db1SRobert Mustacchi static bool
ismt_dma_alloc(ismt_t * ismt,ismt_dma_t * dma,size_t size)807*c1733db1SRobert Mustacchi ismt_dma_alloc(ismt_t *ismt, ismt_dma_t *dma, size_t size)
808*c1733db1SRobert Mustacchi {
809*c1733db1SRobert Mustacchi int ret;
810*c1733db1SRobert Mustacchi ddi_device_acc_attr_t acc;
811*c1733db1SRobert Mustacchi uint_t flags = DDI_DMA_CONSISTENT;
812*c1733db1SRobert Mustacchi
813*c1733db1SRobert Mustacchi bzero(dma, sizeof (ismt_dma_t));
814*c1733db1SRobert Mustacchi ret = ddi_dma_alloc_handle(ismt->ismt_dip, &ismt_dma_attr,
815*c1733db1SRobert Mustacchi DDI_DMA_SLEEP, NULL, &dma->id_hdl);
816*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
817*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "!failed to allocate DMA "
818*c1733db1SRobert Mustacchi "handle: %d", ret);
819*c1733db1SRobert Mustacchi return (false);
820*c1733db1SRobert Mustacchi }
821*c1733db1SRobert Mustacchi
822*c1733db1SRobert Mustacchi bzero(&acc, sizeof (acc));
823*c1733db1SRobert Mustacchi acc.devacc_attr_version = DDI_DEVICE_ATTR_V1;
824*c1733db1SRobert Mustacchi acc.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
825*c1733db1SRobert Mustacchi acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
826*c1733db1SRobert Mustacchi acc.devacc_attr_access = DDI_DEFAULT_ACC;
827*c1733db1SRobert Mustacchi ret = ddi_dma_mem_alloc(dma->id_hdl, size, &acc, flags,
828*c1733db1SRobert Mustacchi DDI_DMA_SLEEP, NULL, &dma->id_va, &dma->id_alloc_len,
829*c1733db1SRobert Mustacchi &dma->id_acc);
830*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
831*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "!failed to allocate %lu "
832*c1733db1SRobert Mustacchi "bytes of DMA memory: %d", size, ret);
833*c1733db1SRobert Mustacchi ismt_dma_free(dma);
834*c1733db1SRobert Mustacchi return (false);
835*c1733db1SRobert Mustacchi }
836*c1733db1SRobert Mustacchi
837*c1733db1SRobert Mustacchi bzero(dma->id_va, dma->id_alloc_len);
838*c1733db1SRobert Mustacchi ret = ddi_dma_addr_bind_handle(dma->id_hdl, NULL, dma->id_va,
839*c1733db1SRobert Mustacchi dma->id_alloc_len, DDI_DMA_RDWR | flags, DDI_DMA_DONTWAIT, NULL,
840*c1733db1SRobert Mustacchi NULL, NULL);
841*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
842*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "!failed to bind %lu bytes of "
843*c1733db1SRobert Mustacchi "DMA memory: %d", dma->id_alloc_len, ret);
844*c1733db1SRobert Mustacchi ismt_dma_free(dma);
845*c1733db1SRobert Mustacchi return (false);
846*c1733db1SRobert Mustacchi }
847*c1733db1SRobert Mustacchi
848*c1733db1SRobert Mustacchi dma->id_size = size;
849*c1733db1SRobert Mustacchi return (true);
850*c1733db1SRobert Mustacchi }
851*c1733db1SRobert Mustacchi
852*c1733db1SRobert Mustacchi static bool
ismt_alloc_intr(ismt_t * ismt)853*c1733db1SRobert Mustacchi ismt_alloc_intr(ismt_t *ismt)
854*c1733db1SRobert Mustacchi {
855*c1733db1SRobert Mustacchi int ret, types;
856*c1733db1SRobert Mustacchi
857*c1733db1SRobert Mustacchi ret = ddi_intr_get_supported_types(ismt->ismt_dip, &types);
858*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
859*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to get supproted "
860*c1733db1SRobert Mustacchi "interrupt types: 0x%d", ret);
861*c1733db1SRobert Mustacchi return (false);
862*c1733db1SRobert Mustacchi }
863*c1733db1SRobert Mustacchi
864*c1733db1SRobert Mustacchi /*
865*c1733db1SRobert Mustacchi * We only expect hardware to support MSIs and INTx.
866*c1733db1SRobert Mustacchi */
867*c1733db1SRobert Mustacchi if ((types & DDI_INTR_TYPE_MSI) != 0) {
868*c1733db1SRobert Mustacchi ret = ddi_intr_alloc(ismt->ismt_dip, &ismt->ismt_intr_hdl,
869*c1733db1SRobert Mustacchi DDI_INTR_TYPE_MSI, 0, 1, &ismt->ismt_nintrs,
870*c1733db1SRobert Mustacchi DDI_INTR_ALLOC_STRICT);
871*c1733db1SRobert Mustacchi if (ret == DDI_SUCCESS) {
872*c1733db1SRobert Mustacchi ismt->ismt_itype = DDI_INTR_TYPE_MSI;
873*c1733db1SRobert Mustacchi return (true);
874*c1733db1SRobert Mustacchi }
875*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "!failed to allocate MSI "
876*c1733db1SRobert Mustacchi "interrupt");
877*c1733db1SRobert Mustacchi }
878*c1733db1SRobert Mustacchi
879*c1733db1SRobert Mustacchi if ((types & DDI_INTR_TYPE_FIXED) != 0) {
880*c1733db1SRobert Mustacchi ret = ddi_intr_alloc(ismt->ismt_dip, &ismt->ismt_intr_hdl,
881*c1733db1SRobert Mustacchi DDI_INTR_TYPE_MSI, 0, 1, &ismt->ismt_nintrs,
882*c1733db1SRobert Mustacchi DDI_INTR_ALLOC_STRICT);
883*c1733db1SRobert Mustacchi if (ret == DDI_SUCCESS) {
884*c1733db1SRobert Mustacchi ismt->ismt_itype = DDI_INTR_TYPE_FIXED;
885*c1733db1SRobert Mustacchi return (true);
886*c1733db1SRobert Mustacchi }
887*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "!failed to allocate INTx "
888*c1733db1SRobert Mustacchi "interrupt");
889*c1733db1SRobert Mustacchi }
890*c1733db1SRobert Mustacchi
891*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to allocate any interrupts "
892*c1733db1SRobert Mustacchi "from type 0x%x", types);
893*c1733db1SRobert Mustacchi return (false);
894*c1733db1SRobert Mustacchi }
895*c1733db1SRobert Mustacchi
896*c1733db1SRobert Mustacchi static bool
ismt_setup_intr(ismt_t * ismt)897*c1733db1SRobert Mustacchi ismt_setup_intr(ismt_t *ismt)
898*c1733db1SRobert Mustacchi {
899*c1733db1SRobert Mustacchi int ret = ddi_intr_add_handler(ismt->ismt_intr_hdl, ismt_intr, ismt,
900*c1733db1SRobert Mustacchi NULL);
901*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
902*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to add interrupt "
903*c1733db1SRobert Mustacchi "handler: 0x%x", ret);
904*c1733db1SRobert Mustacchi return (false);
905*c1733db1SRobert Mustacchi }
906*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_INTR_HDL;
907*c1733db1SRobert Mustacchi
908*c1733db1SRobert Mustacchi ret = ddi_intr_get_pri(ismt->ismt_intr_hdl, &ismt->ismt_intr_pri);
909*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
910*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to get interrupt "
911*c1733db1SRobert Mustacchi "priority");
912*c1733db1SRobert Mustacchi return (false);
913*c1733db1SRobert Mustacchi }
914*c1733db1SRobert Mustacchi
915*c1733db1SRobert Mustacchi return (true);
916*c1733db1SRobert Mustacchi }
917*c1733db1SRobert Mustacchi
918*c1733db1SRobert Mustacchi /*
919*c1733db1SRobert Mustacchi * Go through and set up hardware for use. We need to explicitly take care of:
920*c1733db1SRobert Mustacchi *
921*c1733db1SRobert Mustacchi * - The Interrupt Cause Log
922*c1733db1SRobert Mustacchi * - The Controller DMA ring
923*c1733db1SRobert Mustacchi * - Firmware Head and Tail Registers
924*c1733db1SRobert Mustacchi * - Enabling generation of interrupts
925*c1733db1SRobert Mustacchi *
926*c1733db1SRobert Mustacchi * We use the hardwar defaults for the device retry policy.
927*c1733db1SRobert Mustacchi */
928*c1733db1SRobert Mustacchi static void
ismt_ctrl_init(ismt_t * ismt)929*c1733db1SRobert Mustacchi ismt_ctrl_init(ismt_t *ismt)
930*c1733db1SRobert Mustacchi {
931*c1733db1SRobert Mustacchi uint32_t val;
932*c1733db1SRobert Mustacchi const ddi_dma_cookie_t *c;
933*c1733db1SRobert Mustacchi
934*c1733db1SRobert Mustacchi c = ddi_dma_cookie_one(ismt->ismt_icl_dma.id_hdl);
935*c1733db1SRobert Mustacchi ismt_write64(ismt, ISMT_R_SMTICL, c->dmac_laddress);
936*c1733db1SRobert Mustacchi
937*c1733db1SRobert Mustacchi c = ddi_dma_cookie_one(ismt->ismt_ring_dma.id_hdl);
938*c1733db1SRobert Mustacchi ismt_write64(ismt, ISMT_R_MDBA, c->dmac_laddress);
939*c1733db1SRobert Mustacchi
940*c1733db1SRobert Mustacchi val = ISMT_R_MDS_SET_SIZE(0, ISMT_RING_NENTS - 1);
941*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_MDS, val);
942*c1733db1SRobert Mustacchi
943*c1733db1SRobert Mustacchi val = ISMT_R_MCTRL_SET_FMHP(0, 0);
944*c1733db1SRobert Mustacchi val = ISMT_R_MCTRL_SET_MEIE(val, 1);
945*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_MCTRL, val);
946*c1733db1SRobert Mustacchi
947*c1733db1SRobert Mustacchi val = ISMT_R_MSTS_SET_HMTP(0, 0);
948*c1733db1SRobert Mustacchi ismt_write32(ismt, ISMT_R_MSTS, val);
949*c1733db1SRobert Mustacchi
950*c1733db1SRobert Mustacchi ismt->ismt_head = ismt->ismt_tail = 0;
951*c1733db1SRobert Mustacchi ismt->ismt_ring = (ismt_desc_t *)ismt->ismt_ring_dma.id_va;
952*c1733db1SRobert Mustacchi
953*c1733db1SRobert Mustacchi val = ismt_read32(ismt, ISMT_R_SPGT);
954*c1733db1SRobert Mustacchi switch (ISMT_R_SPGT_GET_SPD(val)) {
955*c1733db1SRobert Mustacchi case ISMT_R_SPT_SPD_80K:
956*c1733db1SRobert Mustacchi case ISMT_R_SPT_SPD_100K:
957*c1733db1SRobert Mustacchi ismt->ismt_speed = I2C_SPEED_STD;
958*c1733db1SRobert Mustacchi break;
959*c1733db1SRobert Mustacchi case ISMT_R_SPT_SPD_400K:
960*c1733db1SRobert Mustacchi ismt->ismt_speed = I2C_SPEED_FAST;
961*c1733db1SRobert Mustacchi break;
962*c1733db1SRobert Mustacchi case ISMT_R_SPT_SPD_1M:
963*c1733db1SRobert Mustacchi ismt->ismt_speed = I2C_SPEED_FPLUS;
964*c1733db1SRobert Mustacchi break;
965*c1733db1SRobert Mustacchi }
966*c1733db1SRobert Mustacchi }
967*c1733db1SRobert Mustacchi
968*c1733db1SRobert Mustacchi static bool
ismt_enable_intr(ismt_t * ismt)969*c1733db1SRobert Mustacchi ismt_enable_intr(ismt_t *ismt)
970*c1733db1SRobert Mustacchi {
971*c1733db1SRobert Mustacchi int ret = ddi_intr_enable(ismt->ismt_intr_hdl);
972*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
973*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to enable interrupt "
974*c1733db1SRobert Mustacchi "handler: %d", ret);
975*c1733db1SRobert Mustacchi return (false);
976*c1733db1SRobert Mustacchi }
977*c1733db1SRobert Mustacchi
978*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_INTR_EN;
979*c1733db1SRobert Mustacchi return (true);
980*c1733db1SRobert Mustacchi }
981*c1733db1SRobert Mustacchi
982*c1733db1SRobert Mustacchi static bool
ismt_register(ismt_t * ismt)983*c1733db1SRobert Mustacchi ismt_register(ismt_t *ismt)
984*c1733db1SRobert Mustacchi {
985*c1733db1SRobert Mustacchi i2c_ctrl_reg_error_t ret;
986*c1733db1SRobert Mustacchi i2c_ctrl_register_t *reg;
987*c1733db1SRobert Mustacchi
988*c1733db1SRobert Mustacchi ret = i2c_ctrl_register_alloc(I2C_CTRL_PROVIDER, ®);
989*c1733db1SRobert Mustacchi if (ret != 0) {
990*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to allocate i2c "
991*c1733db1SRobert Mustacchi "controller registration structure: 0x%x", ret);
992*c1733db1SRobert Mustacchi return (false);
993*c1733db1SRobert Mustacchi }
994*c1733db1SRobert Mustacchi
995*c1733db1SRobert Mustacchi reg->ic_type = I2C_CTRL_TYPE_SMBUS;
996*c1733db1SRobert Mustacchi reg->ic_nports = 1;
997*c1733db1SRobert Mustacchi reg->ic_dip = ismt->ismt_dip;
998*c1733db1SRobert Mustacchi reg->ic_drv = ismt;
999*c1733db1SRobert Mustacchi reg->ic_ops = &ismt_ctrl_ops;
1000*c1733db1SRobert Mustacchi
1001*c1733db1SRobert Mustacchi ret = i2c_ctrl_register(reg, &ismt->ismt_hdl);
1002*c1733db1SRobert Mustacchi i2c_ctrl_register_free(reg);
1003*c1733db1SRobert Mustacchi if (ret != 0) {
1004*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to register with i2c "
1005*c1733db1SRobert Mustacchi "framework: 0x%x", ret);
1006*c1733db1SRobert Mustacchi return (false);
1007*c1733db1SRobert Mustacchi }
1008*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_I2C;
1009*c1733db1SRobert Mustacchi
1010*c1733db1SRobert Mustacchi return (true);
1011*c1733db1SRobert Mustacchi }
1012*c1733db1SRobert Mustacchi
1013*c1733db1SRobert Mustacchi static void
ismt_cleanup(ismt_t * ismt)1014*c1733db1SRobert Mustacchi ismt_cleanup(ismt_t *ismt)
1015*c1733db1SRobert Mustacchi {
1016*c1733db1SRobert Mustacchi if ((ismt->ismt_init & ISMT_INIT_INTR_EN) != 0) {
1017*c1733db1SRobert Mustacchi /*
1018*c1733db1SRobert Mustacchi * If this fails while tearing down, there isn't much we can do.
1019*c1733db1SRobert Mustacchi */
1020*c1733db1SRobert Mustacchi int ret = ddi_intr_disable(ismt->ismt_intr_hdl);
1021*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
1022*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to disable "
1023*c1733db1SRobert Mustacchi "interrupt handler: %d", ret);
1024*c1733db1SRobert Mustacchi }
1025*c1733db1SRobert Mustacchi
1026*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_INTR_EN;
1027*c1733db1SRobert Mustacchi }
1028*c1733db1SRobert Mustacchi
1029*c1733db1SRobert Mustacchi if ((ismt->ismt_init & ISMT_INIT_SYNC) != 0) {
1030*c1733db1SRobert Mustacchi cv_destroy(&ismt->ismt_cv);
1031*c1733db1SRobert Mustacchi mutex_destroy(&ismt->ismt_mutex);
1032*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_SYNC;
1033*c1733db1SRobert Mustacchi }
1034*c1733db1SRobert Mustacchi
1035*c1733db1SRobert Mustacchi if ((ismt->ismt_init & ISMT_INIT_INTR_HDL) != 0) {
1036*c1733db1SRobert Mustacchi int ret = ddi_intr_remove_handler(ismt->ismt_intr_hdl);
1037*c1733db1SRobert Mustacchi if (ret != 0) {
1038*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to remove "
1039*c1733db1SRobert Mustacchi "interrupt handler: 0x%x", ret);
1040*c1733db1SRobert Mustacchi }
1041*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_INTR_HDL;
1042*c1733db1SRobert Mustacchi }
1043*c1733db1SRobert Mustacchi if ((ismt->ismt_init & ISMT_INIT_INTR_ALLOC) != 0) {
1044*c1733db1SRobert Mustacchi int ret = ddi_intr_free(ismt->ismt_intr_hdl);
1045*c1733db1SRobert Mustacchi if (ret != DDI_SUCCESS) {
1046*c1733db1SRobert Mustacchi dev_err(ismt->ismt_dip, CE_WARN, "failed to free "
1047*c1733db1SRobert Mustacchi "device interrupt: 0x%x", ret);
1048*c1733db1SRobert Mustacchi }
1049*c1733db1SRobert Mustacchi
1050*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_INTR_ALLOC;
1051*c1733db1SRobert Mustacchi }
1052*c1733db1SRobert Mustacchi
1053*c1733db1SRobert Mustacchi ismt_dma_free(&ismt->ismt_icl_dma);
1054*c1733db1SRobert Mustacchi ismt_dma_free(&ismt->ismt_data_dma);
1055*c1733db1SRobert Mustacchi ismt_dma_free(&ismt->ismt_ring_dma);
1056*c1733db1SRobert Mustacchi
1057*c1733db1SRobert Mustacchi if ((ismt->ismt_init & ISMT_INIT_REGS) != 0) {
1058*c1733db1SRobert Mustacchi ddi_regs_map_free(&ismt->ismt_regs);
1059*c1733db1SRobert Mustacchi ismt->ismt_regs = NULL;
1060*c1733db1SRobert Mustacchi ismt->ismt_regsize = 0;
1061*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_REGS;
1062*c1733db1SRobert Mustacchi }
1063*c1733db1SRobert Mustacchi
1064*c1733db1SRobert Mustacchi if ((ismt->ismt_init & ISMT_INIT_PCI) != 0) {
1065*c1733db1SRobert Mustacchi pci_config_teardown(&ismt->ismt_cfg);
1066*c1733db1SRobert Mustacchi ismt->ismt_cfg = NULL;
1067*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_PCI;
1068*c1733db1SRobert Mustacchi }
1069*c1733db1SRobert Mustacchi
1070*c1733db1SRobert Mustacchi ASSERT0(ismt->ismt_init);
1071*c1733db1SRobert Mustacchi ddi_set_driver_private(ismt->ismt_dip, NULL);
1072*c1733db1SRobert Mustacchi kmem_free(ismt, sizeof (ismt_t));
1073*c1733db1SRobert Mustacchi }
1074*c1733db1SRobert Mustacchi
1075*c1733db1SRobert Mustacchi int
ismt_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1076*c1733db1SRobert Mustacchi ismt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1077*c1733db1SRobert Mustacchi {
1078*c1733db1SRobert Mustacchi ismt_t *ismt;
1079*c1733db1SRobert Mustacchi
1080*c1733db1SRobert Mustacchi switch (cmd) {
1081*c1733db1SRobert Mustacchi case DDI_ATTACH:
1082*c1733db1SRobert Mustacchi break;
1083*c1733db1SRobert Mustacchi case DDI_RESUME:
1084*c1733db1SRobert Mustacchi default:
1085*c1733db1SRobert Mustacchi return (DDI_FAILURE);
1086*c1733db1SRobert Mustacchi }
1087*c1733db1SRobert Mustacchi
1088*c1733db1SRobert Mustacchi ismt = kmem_zalloc(sizeof (ismt_t), KM_SLEEP);
1089*c1733db1SRobert Mustacchi ismt->ismt_dip = dip;
1090*c1733db1SRobert Mustacchi ddi_set_driver_private(dip, ismt);
1091*c1733db1SRobert Mustacchi
1092*c1733db1SRobert Mustacchi if (pci_config_setup(dip, &ismt->ismt_cfg) != DDI_SUCCESS) {
1093*c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to set up config space");
1094*c1733db1SRobert Mustacchi goto cleanup;
1095*c1733db1SRobert Mustacchi }
1096*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_PCI;
1097*c1733db1SRobert Mustacchi
1098*c1733db1SRobert Mustacchi if (!ismt_setup_regs(ismt))
1099*c1733db1SRobert Mustacchi goto cleanup;
1100*c1733db1SRobert Mustacchi
1101*c1733db1SRobert Mustacchi if (!ismt_dma_alloc(ismt, &ismt->ismt_ring_dma, ISMT_RING_DMA_SIZE)) {
1102*c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to allocate ring DMA memory");
1103*c1733db1SRobert Mustacchi goto cleanup;
1104*c1733db1SRobert Mustacchi }
1105*c1733db1SRobert Mustacchi
1106*c1733db1SRobert Mustacchi if (!ismt_dma_alloc(ismt, &ismt->ismt_data_dma, ISMT_DATA_BUF_SIZE)) {
1107*c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to allocate data buffer DMA "
1108*c1733db1SRobert Mustacchi "memory");
1109*c1733db1SRobert Mustacchi goto cleanup;
1110*c1733db1SRobert Mustacchi }
1111*c1733db1SRobert Mustacchi
1112*c1733db1SRobert Mustacchi if (!ismt_dma_alloc(ismt, &ismt->ismt_icl_dma, ISMT_ICL_DMA_SIZE)) {
1113*c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to allocate interrupt cause DMA "
1114*c1733db1SRobert Mustacchi "memory");
1115*c1733db1SRobert Mustacchi goto cleanup;
1116*c1733db1SRobert Mustacchi }
1117*c1733db1SRobert Mustacchi
1118*c1733db1SRobert Mustacchi if (!ismt_alloc_intr(ismt))
1119*c1733db1SRobert Mustacchi goto cleanup;
1120*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_INTR_ALLOC;
1121*c1733db1SRobert Mustacchi
1122*c1733db1SRobert Mustacchi if (!ismt_setup_intr(ismt))
1123*c1733db1SRobert Mustacchi goto cleanup;
1124*c1733db1SRobert Mustacchi
1125*c1733db1SRobert Mustacchi mutex_init(&ismt->ismt_mutex, NULL, MUTEX_DRIVER,
1126*c1733db1SRobert Mustacchi DDI_INTR_PRI(ismt->ismt_intr_pri));
1127*c1733db1SRobert Mustacchi cv_init(&ismt->ismt_cv, NULL, CV_DRIVER, NULL);
1128*c1733db1SRobert Mustacchi ismt->ismt_init |= ISMT_INIT_SYNC;
1129*c1733db1SRobert Mustacchi
1130*c1733db1SRobert Mustacchi ismt_ctrl_init(ismt);
1131*c1733db1SRobert Mustacchi
1132*c1733db1SRobert Mustacchi if (!ismt_enable_intr(ismt))
1133*c1733db1SRobert Mustacchi goto cleanup;
1134*c1733db1SRobert Mustacchi
1135*c1733db1SRobert Mustacchi if (!ismt_register(ismt))
1136*c1733db1SRobert Mustacchi goto cleanup;
1137*c1733db1SRobert Mustacchi
1138*c1733db1SRobert Mustacchi return (DDI_SUCCESS);
1139*c1733db1SRobert Mustacchi
1140*c1733db1SRobert Mustacchi cleanup:
1141*c1733db1SRobert Mustacchi ismt_cleanup(ismt);
1142*c1733db1SRobert Mustacchi return (DDI_FAILURE);
1143*c1733db1SRobert Mustacchi }
1144*c1733db1SRobert Mustacchi
1145*c1733db1SRobert Mustacchi int
ismt_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1146*c1733db1SRobert Mustacchi ismt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1147*c1733db1SRobert Mustacchi {
1148*c1733db1SRobert Mustacchi ismt_t *ismt;
1149*c1733db1SRobert Mustacchi
1150*c1733db1SRobert Mustacchi switch (cmd) {
1151*c1733db1SRobert Mustacchi case DDI_DETACH:
1152*c1733db1SRobert Mustacchi break;
1153*c1733db1SRobert Mustacchi case DDI_SUSPEND:
1154*c1733db1SRobert Mustacchi default:
1155*c1733db1SRobert Mustacchi return (DDI_FAILURE);
1156*c1733db1SRobert Mustacchi }
1157*c1733db1SRobert Mustacchi
1158*c1733db1SRobert Mustacchi ismt = ddi_get_driver_private(dip);
1159*c1733db1SRobert Mustacchi if (ismt == NULL) {
1160*c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "asked to detach, but missing private "
1161*c1733db1SRobert Mustacchi "data");
1162*c1733db1SRobert Mustacchi return (DDI_FAILURE);
1163*c1733db1SRobert Mustacchi }
1164*c1733db1SRobert Mustacchi
1165*c1733db1SRobert Mustacchi VERIFY3P(ismt->ismt_dip, ==, dip);
1166*c1733db1SRobert Mustacchi i2c_ctrl_reg_error_t ret = i2c_ctrl_unregister(ismt->ismt_hdl);
1167*c1733db1SRobert Mustacchi if (ret != 0) {
1168*c1733db1SRobert Mustacchi dev_err(dip, CE_WARN, "failed to unregister from i2c "
1169*c1733db1SRobert Mustacchi "framework 0x%x", ret);
1170*c1733db1SRobert Mustacchi return (DDI_FAILURE);
1171*c1733db1SRobert Mustacchi }
1172*c1733db1SRobert Mustacchi ismt->ismt_init &= ~ISMT_INIT_I2C;
1173*c1733db1SRobert Mustacchi ismt_cleanup(ismt);
1174*c1733db1SRobert Mustacchi
1175*c1733db1SRobert Mustacchi return (DDI_SUCCESS);
1176*c1733db1SRobert Mustacchi }
1177*c1733db1SRobert Mustacchi
1178*c1733db1SRobert Mustacchi static struct dev_ops ismt_dev_ops = {
1179*c1733db1SRobert Mustacchi .devo_rev = DEVO_REV,
1180*c1733db1SRobert Mustacchi .devo_refcnt = 0,
1181*c1733db1SRobert Mustacchi .devo_identify = nulldev,
1182*c1733db1SRobert Mustacchi .devo_probe = nulldev,
1183*c1733db1SRobert Mustacchi .devo_attach = ismt_attach,
1184*c1733db1SRobert Mustacchi .devo_detach = ismt_detach,
1185*c1733db1SRobert Mustacchi .devo_reset = nodev,
1186*c1733db1SRobert Mustacchi .devo_quiesce = ddi_quiesce_not_supported,
1187*c1733db1SRobert Mustacchi };
1188*c1733db1SRobert Mustacchi
1189*c1733db1SRobert Mustacchi static struct modldrv ismt_modldrv = {
1190*c1733db1SRobert Mustacchi .drv_modops = &mod_driverops,
1191*c1733db1SRobert Mustacchi .drv_linkinfo = "Intel SMBus Message Target",
1192*c1733db1SRobert Mustacchi .drv_dev_ops = &ismt_dev_ops
1193*c1733db1SRobert Mustacchi };
1194*c1733db1SRobert Mustacchi
1195*c1733db1SRobert Mustacchi static struct modlinkage ismt_modlinkage = {
1196*c1733db1SRobert Mustacchi .ml_rev = MODREV_1,
1197*c1733db1SRobert Mustacchi .ml_linkage = { &ismt_modldrv, NULL }
1198*c1733db1SRobert Mustacchi };
1199*c1733db1SRobert Mustacchi
1200*c1733db1SRobert Mustacchi int
_init(void)1201*c1733db1SRobert Mustacchi _init(void)
1202*c1733db1SRobert Mustacchi {
1203*c1733db1SRobert Mustacchi int ret;
1204*c1733db1SRobert Mustacchi
1205*c1733db1SRobert Mustacchi i2c_ctrl_mod_init(&ismt_dev_ops);
1206*c1733db1SRobert Mustacchi if ((ret = mod_install(&ismt_modlinkage)) != 0) {
1207*c1733db1SRobert Mustacchi i2c_ctrl_mod_fini(&ismt_dev_ops);
1208*c1733db1SRobert Mustacchi }
1209*c1733db1SRobert Mustacchi
1210*c1733db1SRobert Mustacchi return (ret);
1211*c1733db1SRobert Mustacchi }
1212*c1733db1SRobert Mustacchi
1213*c1733db1SRobert Mustacchi int
_info(struct modinfo * modinfop)1214*c1733db1SRobert Mustacchi _info(struct modinfo *modinfop)
1215*c1733db1SRobert Mustacchi {
1216*c1733db1SRobert Mustacchi return (mod_info(&ismt_modlinkage, modinfop));
1217*c1733db1SRobert Mustacchi }
1218*c1733db1SRobert Mustacchi
1219*c1733db1SRobert Mustacchi int
_fini(void)1220*c1733db1SRobert Mustacchi _fini(void)
1221*c1733db1SRobert Mustacchi {
1222*c1733db1SRobert Mustacchi int ret;
1223*c1733db1SRobert Mustacchi
1224*c1733db1SRobert Mustacchi if ((ret = mod_remove(&ismt_modlinkage)) == 0) {
1225*c1733db1SRobert Mustacchi i2c_ctrl_mod_fini(&ismt_dev_ops);
1226*c1733db1SRobert Mustacchi }
1227*c1733db1SRobert Mustacchi
1228*c1733db1SRobert Mustacchi return (ret);
1229*c1733db1SRobert Mustacchi }
1230